aboutsummaryrefslogtreecommitdiff
path: root/buildroot/share/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'buildroot/share/scripts')
-rw-r--r--buildroot/share/scripts/MarlinMesh.scad304
-rw-r--r--buildroot/share/scripts/config-labels.py201
-rw-r--r--buildroot/share/scripts/createSpeedLookupTable.py53
-rw-r--r--buildroot/share/scripts/createTemperatureLookupMarlin.py158
-rw-r--r--buildroot/share/scripts/findMissingTranslations.sh49
-rw-r--r--buildroot/share/scripts/g29_auto.py188
-rw-r--r--buildroot/share/scripts/pinsformat.js170
7 files changed, 1123 insertions, 0 deletions
diff --git a/buildroot/share/scripts/MarlinMesh.scad b/buildroot/share/scripts/MarlinMesh.scad
new file mode 100644
index 0000000..6d78bb9
--- /dev/null
+++ b/buildroot/share/scripts/MarlinMesh.scad
@@ -0,0 +1,304 @@
+ /**************************************\
+ * *
+ * OpenSCAD Mesh Display *
+ * by Thinkyhead - April 2017 *
+ * *
+ * Copy the grid output from Marlin, *
+ * paste below as shown, and use *
+ * OpenSCAD to see a visualization *
+ * of your mesh. *
+ * *
+ \**************************************/
+
+$t = 0.15; // comment out during animation!
+X = 0; Y = 1;
+L = 0; R = 1; F = 2; B = 3;
+
+//
+// Sample Mesh - Replace with your own
+//
+measured_z = [
+ [ -1.20, -1.13, -1.09, -1.03, -1.19 ],
+ [ -1.16, -1.25, -1.27, -1.25, -1.08 ],
+ [ -1.13, -1.26, -1.39, -1.31, -1.18 ],
+ [ -1.09, -1.20, -1.26, -1.21, -1.18 ],
+ [ -1.13, -0.99, -1.03, -1.06, -1.32 ]
+];
+
+//
+// An offset to add to all points in the mesh
+//
+zadjust = 0;
+
+//
+// Mesh characteristics
+//
+bed_size = [ 200, 200 ];
+
+mesh_inset = [ 10, 10, 10, 10 ]; // L, F, R, B
+
+mesh_bounds = [
+ [ mesh_inset[L], mesh_inset[F] ],
+ [ bed_size[X] - mesh_inset[R], bed_size[Y] - mesh_inset[B] ]
+];
+
+mesh_size = mesh_bounds[1] - mesh_bounds[0];
+
+ // NOTE: Marlin meshes already subtract the probe offset
+NAN = 0; // Z to use for un-measured points
+
+//
+// Geometry
+//
+
+max_z_scale = 100; // Scale at Time 0.5
+min_z_scale = 10; // Scale at Time 0.0 and 1.0
+thickness = 0.5; // thickness of the mesh triangles
+tesselation = 1; // levels of tesselation from 0-2
+alternation = 2; // direction change modulus (try it)
+
+//
+// Appearance
+//
+
+show_plane = true;
+show_labels = true;
+show_coords = true;
+arrow_length = 5;
+
+label_font_lg = "Arial";
+label_font_sm = "Arial";
+mesh_color = [1,1,1,0.5];
+plane_color = [0.4,0.6,0.9,0.6];
+
+//================================================ Derive useful values
+
+big_z = max_2D(measured_z,0);
+lil_z = min_2D(measured_z,0);
+
+mean_value = (big_z + lil_z) / 2.0;
+
+mesh_points_y = len(measured_z);
+mesh_points_x = len(measured_z[0]);
+
+xspace = mesh_size[X] / (mesh_points_x - 1);
+yspace = mesh_size[Y] / (mesh_points_y - 1);
+
+// At $t=0 and $t=1 scale will be 100%
+z_scale_factor = min_z_scale + (($t > 0.5) ? 1.0 - $t : $t) * (max_z_scale - min_z_scale) * 2;
+
+//
+// Min and max recursive functions for 1D and 2D arrays
+// Return the smallest or largest value in the array
+//
+function some_1D(b,i) = (i<len(b)-1) ? (b[i] && some_1D(b,i+1)) : b[i] != 0;
+function some_2D(a,j) = (j<len(a)-1) ? some_2D(a,j+1) : some_1D(a[j], 0);
+function min_1D(b,i) = (i<len(b)-1) ? min(b[i], min_1D(b,i+1)) : b[i];
+function min_2D(a,j) = (j<len(a)-1) ? min_2D(a,j+1) : min_1D(a[j], 0);
+function max_1D(b,i) = (i<len(b)-1) ? max(b[i], max_1D(b,i+1)) : b[i];
+function max_2D(a,j) = (j<len(a)-1) ? max_2D(a,j+1) : max_1D(a[j], 0);
+
+//
+// Get the corner probe points of a grid square.
+//
+// Input : x,y grid indexes
+// Output : An array of the 4 corner points
+//
+function grid_square(x,y) = [
+ [x * xspace, y * yspace, z_scale_factor * (measured_z[y][x] - mean_value)],
+ [x * xspace, (y+1) * yspace, z_scale_factor * (measured_z[y+1][x] - mean_value)],
+ [(x+1) * xspace, (y+1) * yspace, z_scale_factor * (measured_z[y+1][x+1] - mean_value)],
+ [(x+1) * xspace, y * yspace, z_scale_factor * (measured_z[y][x+1] - mean_value)]
+];
+
+// The corner point of a grid square with Z centered on the mean
+function pos(x,y,z) = [x * xspace, y * yspace, z_scale_factor * (z - mean_value)];
+
+//
+// Draw the point markers and labels
+//
+module point_markers(show_home=true) {
+ // Mark the home position 0,0
+ if (show_home)
+ translate([1,1]) color([0,0,0,0.25])
+ cylinder(r=1, h=z_scale_factor, center=true);
+
+ for (x=[0:mesh_points_x-1], y=[0:mesh_points_y-1]) {
+ z = measured_z[y][x] - zadjust;
+ down = z < mean_value;
+ xyz = pos(x, y, z);
+ translate([ xyz[0], xyz[1] ]) {
+
+ // Show the XY as well as the Z!
+ if (show_coords) {
+ color("black")
+ translate([0,0,0.5]) {
+ $fn=8;
+ rotate([0,0]) {
+ posx = floor(mesh_bounds[0][X] + x * xspace);
+ posy = floor(mesh_bounds[0][Y] + y * yspace);
+ text(str(posx, ",", posy), 2, label_font_sm, halign="center", valign="center");
+ }
+ }
+ }
+
+ translate([ 0, 0, xyz[2] ]) {
+ // Label each point with the Z
+ v = z - mean_value;
+ if (show_labels) {
+
+ color(abs(v) < 0.1 ? [0,0.5,0] : [0.25,0,0])
+ translate([0,0,down?-10:10]) {
+
+ $fn=8;
+ rotate([90,0])
+ text(str(z), 6, label_font_lg, halign="center", valign="center");
+
+ if (v)
+ translate([0,0,down?-6:6]) rotate([90,0])
+ text(str(down || !v ? "" : "+", v), 3, label_font_sm, halign="center", valign="center");
+ }
+ }
+
+ // Show an arrow pointing up or down
+ if (v) {
+ rotate([0, down ? 180 : 0]) translate([0,0,-1])
+ cylinder(
+ r1=0.5,
+ r2=0.1,
+ h=arrow_length, $fn=12, center=1
+ );
+ }
+ else
+ color([1,0,1,0.4]) sphere(r=1.0, $fn=20, center=1);
+ }
+ }
+ }
+}
+
+//
+// Split a square on the diagonal into
+// two triangles and render them.
+//
+// s : a square
+// alt : a flag to split on the other diagonal
+//
+module tesselated_square(s, alt=false) {
+ add = [0,0,thickness];
+ p1 = [
+ s[0], s[1], s[2], s[3],
+ s[0]+add, s[1]+add, s[2]+add, s[3]+add
+ ];
+ f1 = alt
+ ? [ [0,1,3], [4,5,1,0], [4,7,5], [5,7,3,1], [7,4,0,3] ]
+ : [ [0,1,2], [4,5,1,0], [4,6,5], [5,6,2,1], [6,4,0,2] ];
+ f2 = alt
+ ? [ [1,2,3], [5,6,2,1], [5,6,7], [6,7,3,2], [7,5,1,3] ]
+ : [ [0,2,3], [4,6,2,0], [4,7,6], [6,7,3,2], [7,4,0,3] ];
+
+ // Use the other diagonal
+ polyhedron(points=p1, faces=f1);
+ polyhedron(points=p1, faces=f2);
+}
+
+/**
+ * The simplest mesh display
+ */
+module simple_mesh(show_plane=show_plane) {
+ if (show_plane) color(plane_color) cube([mesh_size[X], mesh_size[Y], thickness]);
+ color(mesh_color)
+ for (x=[0:mesh_points_x-2], y=[0:mesh_points_y-2])
+ tesselated_square(grid_square(x, y));
+}
+
+/**
+ * Subdivide the mesh into smaller squares.
+ */
+module bilinear_mesh(show_plane=show_plane,tesselation=tesselation) {
+ if (show_plane) color(plane_color) translate([-5,-5]) cube([mesh_size[X]+10, mesh_size[Y]+10, thickness]);
+
+ if (some_2D(measured_z, 0)) {
+
+ tesselation = tesselation % 4;
+ color(mesh_color)
+ for (x=[0:mesh_points_x-2], y=[0:mesh_points_y-2]) {
+ square = grid_square(x, y);
+ if (tesselation < 1) {
+ tesselated_square(square,(x%alternation)-(y%alternation));
+ }
+ else {
+ subdiv_4 = subdivided_square(square);
+ if (tesselation < 2) {
+ for (i=[0:3]) tesselated_square(subdiv_4[i],i%alternation);
+ }
+ else {
+ for (i=[0:3]) {
+ subdiv_16 = subdivided_square(subdiv_4[i]);
+ if (tesselation < 3) {
+ for (j=[0:3]) tesselated_square(subdiv_16[j],j%alternation);
+ }
+ else {
+ for (j=[0:3]) {
+ subdiv_64 = subdivided_square(subdiv_16[j]);
+ if (tesselation < 4) {
+ for (k=[0:3]) tesselated_square(subdiv_64[k]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+}
+
+//
+// Subdivision helpers
+//
+function ctrz(a) = (a[0][2]+a[1][2]+a[3][2]+a[2][2])/4;
+function avgx(a,i) = (a[i][0]+a[(i+1)%4][0])/2;
+function avgy(a,i) = (a[i][1]+a[(i+1)%4][1])/2;
+function avgz(a,i) = (a[i][2]+a[(i+1)%4][2])/2;
+
+//
+// Convert one square into 4, applying bilinear averaging
+//
+// Input : 1 square (4 points)
+// Output : An array of 4 squares
+//
+function subdivided_square(a) = [
+ [ // SW square
+ a[0], // SW
+ [a[0][0],avgy(a,0),avgz(a,0)], // CW
+ [avgx(a,1),avgy(a,0),ctrz(a)], // CC
+ [avgx(a,1),a[0][1],avgz(a,3)] // SC
+ ],
+ [ // NW square
+ [a[0][0],avgy(a,0),avgz(a,0)], // CW
+ a[1], // NW
+ [avgx(a,1),a[1][1],avgz(a,1)], // NC
+ [avgx(a,1),avgy(a,0),ctrz(a)] // CC
+ ],
+ [ // NE square
+ [avgx(a,1),avgy(a,0),ctrz(a)], // CC
+ [avgx(a,1),a[1][1],avgz(a,1)], // NC
+ a[2], // NE
+ [a[2][0],avgy(a,0),avgz(a,2)] // CE
+ ],
+ [ // SE square
+ [avgx(a,1),a[0][1],avgz(a,3)], // SC
+ [avgx(a,1),avgy(a,0),ctrz(a)], // CC
+ [a[2][0],avgy(a,0),avgz(a,2)], // CE
+ a[3] // SE
+ ]
+];
+
+
+//================================================ Run the plan
+
+translate([-mesh_size[X] / 2, -mesh_size[Y] / 2]) {
+ $fn = 12;
+ point_markers();
+ bilinear_mesh();
+}
diff --git a/buildroot/share/scripts/config-labels.py b/buildroot/share/scripts/config-labels.py
new file mode 100644
index 0000000..267aa2d
--- /dev/null
+++ b/buildroot/share/scripts/config-labels.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python3
+#
+# for python3.5 or higher
+#-----------------------------------
+# Within Marlin project MarlinFirmware/Configurations, this program visits all folders
+# under .../config/examples/*, processing each Configuration.h, Configuration_adv.h,
+# _Bootscreen.h, and _Statusscreen.h, to insert:
+# #define CONFIG_EXAMPLES_DIR "examples/<style>/<vendor>/<model>"
+# ... or similar path leading to this file.
+#
+# Warning: The program modifies files in place, so be sure to back them up first if needed.
+# Can be run multiple times if needed. Only modifies files which don't have
+# correct #define CONFIG_EXAMPLES_DIR line.
+#
+# Invocation:
+#-------------
+# 1. Change directory to your MarlinFirmware/Configurations working copy
+# 2. python3 config-labels.py
+#
+#-----------------------------------
+# 2020-05-10 GMW original
+# 2020-06-05 SRL style tweaks
+#-----------------------------------
+#
+import sys
+import os
+from pathlib import Path
+from distutils.dir_util import copy_tree # for copy_tree, because shutil.copytree can't handle existing files, dirs
+
+# Modify input_examples_dir and output_examples_dir for your installation
+# No trailing slash
+# Setting output_examples_dir = input_examples_dir causes the program to insert into the existing files.
+
+input_examples_dir = r'config/examples'
+# output_examples_dir = input_examples_dir
+output_examples_dir = r'config/examples'
+
+#-------------------------------------
+
+files_to_mod = ['Configuration.h', 'Configuration_adv.h', '_Bootscreen.h', '_Statusscreen.h']
+
+macro_name = 'CONFIG_EXAMPLES_DIR'
+def_macro_name = '#define ' + macro_name
+
+filenum = 0
+different_out_dir = not (output_examples_dir == input_examples_dir)
+
+#----------------------------------------------
+def process_file(subdir: str, filename: str):
+#----------------------------------------------
+ global filenum
+ filenum += 1
+
+ print(str(filenum) + ' ' + filename + ': ' + subdir)
+
+ def_line = (def_macro_name + ' "' + subdir.replace('\\', '/') + '"')
+
+ #------------------------
+ # Read file
+ #------------------------
+ lines = []
+ infilepath = os.path.join(input_examples_dir, subdir, filename)
+ try:
+ # UTF-8 because some files contain unicode chars
+ with open(infilepath, 'rt', encoding="utf-8") as infile:
+ lines = infile.readlines()
+
+ except Exception as e:
+ print('Failed to read file: ' + str(e) )
+ raise Exception
+
+ lines = [line.rstrip('\r\n') for line in lines]
+
+ #------------------------
+ # Process lines
+ #------------------------
+ file_modified = False
+
+ # region state machine
+ # -1 = before pragma once;
+ # 0 = region to place define;
+ # 1 = past region to place define
+ region = -1
+
+ outlines = []
+ for line in lines:
+ outline = line
+
+ if (region == -1) and (def_macro_name in line):
+ outline = None
+ file_modified = True
+
+ elif (region == -1) and ('pragma once' in line):
+ region = 0
+
+ elif (region == 0):
+ if (line.strip() == ''):
+ pass
+ elif (def_macro_name in line):
+ region = 1
+ if line == def_line: # leave it as is
+ pass
+ else:
+ outline = def_line
+ file_modified = True
+ else: # some other string
+ outlines.append(def_line)
+ outlines.append('')
+ region = 1
+ file_modified = True
+
+ elif (region == 1):
+ if (def_macro_name in line):
+ outline = None
+ file_modified = True
+ else:
+ pass
+
+ # end if
+ if outline is not None:
+ outlines.append(outline)
+ # end for
+
+ #-------------------------
+ # Output file
+ #-------------------------
+ outdir = os.path.join(output_examples_dir, subdir)
+ outfilepath = os.path.join(outdir, filename)
+
+ if file_modified:
+ # Note: no need to create output dirs, as the initial copy_tree
+ # will do that.
+
+ print(' writing ' + str(outfilepath))
+ try:
+ # Preserve unicode chars; Avoid CR-LF on Windows.
+ with open(outfilepath, "w", encoding="utf-8", newline='\n') as outfile:
+ outfile.write("\n".join(outlines))
+ outfile.write("\n")
+
+ except Exception as e:
+ print('Failed to write file: ' + str(e) )
+ raise Exception
+ else:
+ print(' no change for ' + str(outfilepath))
+
+#----------
+def main():
+#----------
+ global filenum
+ global input_examples_dir
+ global output_examples_dir
+ filenum = 0
+
+ #--------------------------------
+ # Check for requirements
+ #--------------------------------
+ input_examples_dir = input_examples_dir.strip()
+ input_examples_dir = input_examples_dir.rstrip('\\/')
+ output_examples_dir = output_examples_dir.strip()
+ output_examples_dir = output_examples_dir.rstrip('\\/')
+
+ for dir in [input_examples_dir, output_examples_dir]:
+ if not (os.path.exists(dir)):
+ print('Directory not found: ' + dir)
+ sys.exit(1)
+
+ #--------------------------------
+ # Copy tree if necessary.
+ #--------------------------------
+ # This includes files that are not otherwise included in the
+ # insertion of the define statement.
+ #
+ if different_out_dir:
+ print('Copying files to new directory: ' + output_examples_dir)
+ try:
+ copy_tree(input_examples_dir, output_examples_dir)
+ except Exception as e:
+ print('Failed to copy directory: ' + str(e) )
+ raise Exception
+
+ #-----------------------------
+ # Find and process files
+ #-----------------------------
+ len_input_examples_dir = len(input_examples_dir);
+ len_input_examples_dir += 1
+
+ for filename in files_to_mod:
+ input_path = Path(input_examples_dir)
+ filepathlist = input_path.rglob(filename)
+
+ for filepath in filepathlist:
+ fulldirpath = str(filepath.parent)
+ subdir = fulldirpath[len_input_examples_dir:]
+
+ process_file(subdir, filename)
+
+#==============
+print('--- Starting config-labels ---')
+main()
+print('--- Done ---')
diff --git a/buildroot/share/scripts/createSpeedLookupTable.py b/buildroot/share/scripts/createSpeedLookupTable.py
new file mode 100644
index 0000000..da24c7c
--- /dev/null
+++ b/buildroot/share/scripts/createSpeedLookupTable.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+from __future__ import division
+
+""" Generate the stepper delay lookup table for Marlin firmware. """
+
+import argparse
+
+__author__ = "Ben Gamari <bgamari@gmail.com>"
+__copyright__ = "Copyright 2012, Ben Gamari"
+__license__ = "GPL"
+
+parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument('-f', '--cpu-freq', type=int, default=16, help='CPU clockrate in MHz (default=16)')
+parser.add_argument('-d', '--divider', type=int, default=8, help='Timer/counter pre-scale divider (default=8)')
+args = parser.parse_args()
+
+cpu_freq = args.cpu_freq * 1000000
+timer_freq = cpu_freq / args.divider
+
+print("#ifndef SPEED_LOOKUPTABLE_H")
+print("#define SPEED_LOOKUPTABLE_H")
+print()
+print('#include "MarlinCore.h"')
+print()
+
+print("const uint16_t speed_lookuptable_fast[256][2] PROGMEM = {")
+a = [ timer_freq / ((i*256)+(args.cpu_freq*2)) for i in range(256) ]
+b = [ a[i] - a[i+1] for i in range(255) ]
+b.append(b[-1])
+for i in range(32):
+ print(" ", end=' ')
+ for j in range(8):
+ print("{%d, %d}," % (a[8*i+j], b[8*i+j]), end=' ')
+ print()
+print("};")
+print()
+
+print("const uint16_t speed_lookuptable_slow[256][2] PROGMEM = {")
+a = [ timer_freq / ((i*8)+(args.cpu_freq*2)) for i in range(256) ]
+b = [ a[i] - a[i+1] for i in range(255) ]
+b.append(b[-1])
+for i in range(32):
+ print(" ", end=' ')
+ for j in range(8):
+ print("{%d, %d}," % (a[8*i+j], b[8*i+j]), end=' ')
+ print()
+print("};")
+print()
+
+print("#endif")
+
diff --git a/buildroot/share/scripts/createTemperatureLookupMarlin.py b/buildroot/share/scripts/createTemperatureLookupMarlin.py
new file mode 100644
index 0000000..b2d8964
--- /dev/null
+++ b/buildroot/share/scripts/createTemperatureLookupMarlin.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+"""Thermistor Value Lookup Table Generator
+
+Generates lookup to temperature values for use in a microcontroller in C format based on:
+https://en.wikipedia.org/wiki/Steinhart-Hart_equation
+
+The main use is for Arduino programs that read data from the circuit board described here:
+https://reprap.org/wiki/Temperature_Sensor_v2.0
+
+Usage: python createTemperatureLookupMarlin.py [options]
+
+Options:
+ -h, --help show this help
+ --rp=... pull-up resistor
+ --t1=ttt:rrr low temperature temperature:resistance point (around 25 degC)
+ --t2=ttt:rrr middle temperature temperature:resistance point (around 150 degC)
+ --t3=ttt:rrr high temperature temperature:resistance point (around 250 degC)
+ --num-temps=... the number of temperature points to calculate (default: 36)
+"""
+
+from __future__ import print_function
+from __future__ import division
+
+from math import *
+import sys
+import getopt
+
+"Constants"
+ZERO = 273.15 # zero point of Kelvin scale
+VADC = 5 # ADC voltage
+VCC = 5 # supply voltage
+ARES = pow(2,10) # 10 Bit ADC resolution
+VSTEP = VADC / ARES # ADC voltage resolution
+TMIN = 0 # lowest temperature in table
+TMAX = 350 # highest temperature in table
+
+class Thermistor:
+ "Class to do the thermistor maths"
+ def __init__(self, rp, t1, r1, t2, r2, t3, r3):
+ l1 = log(r1)
+ l2 = log(r2)
+ l3 = log(r3)
+ y1 = 1.0 / (t1 + ZERO) # adjust scale
+ y2 = 1.0 / (t2 + ZERO)
+ y3 = 1.0 / (t3 + ZERO)
+ x = (y2 - y1) / (l2 - l1)
+ y = (y3 - y1) / (l3 - l1)
+ c = (y - x) / ((l3 - l2) * (l1 + l2 + l3))
+ b = x - c * (l1**2 + l2**2 + l1*l2)
+ a = y1 - (b + l1**2 *c)*l1
+
+ if c < 0:
+ print("//////////////////////////////////////////////////////////////////////////////////////")
+ print("// WARNING: negative coefficient 'c'! Something may be wrong with the measurements! //")
+ print("//////////////////////////////////////////////////////////////////////////////////////")
+ c = -c
+ self.c1 = a # Steinhart-Hart coefficients
+ self.c2 = b
+ self.c3 = c
+ self.rp = rp # pull-up resistance
+
+ def resol(self, adc):
+ "Convert ADC reading into a resolution"
+ res = self.temp(adc)-self.temp(adc+1)
+ return res
+
+ def voltage(self, adc):
+ "Convert ADC reading into a Voltage"
+ return adc * VSTEP # convert the 10 bit ADC value to a voltage
+
+ def resist(self, adc):
+ "Convert ADC reading into a resistance in Ohms"
+ r = self.rp * self.voltage(adc) / (VCC - self.voltage(adc)) # resistance of thermistor
+ return r
+
+ def temp(self, adc):
+ "Convert ADC reading into a temperature in Celcius"
+ l = log(self.resist(adc))
+ Tinv = self.c1 + self.c2*l + self.c3* l**3 # inverse temperature
+ return (1/Tinv) - ZERO # temperature
+
+ def adc(self, temp):
+ "Convert temperature into a ADC reading"
+ x = (self.c1 - (1.0 / (temp+ZERO))) / (2*self.c3)
+ y = sqrt((self.c2 / (3*self.c3))**3 + x**2)
+ r = exp((y-x)**(1.0/3) - (y+x)**(1.0/3))
+ return (r / (self.rp + r)) * ARES
+
+def main(argv):
+ "Default values"
+ t1 = 25 # low temperature in Kelvin (25 degC)
+ r1 = 100000 # resistance at low temperature (10 kOhm)
+ t2 = 150 # middle temperature in Kelvin (150 degC)
+ r2 = 1641.9 # resistance at middle temperature (1.6 KOhm)
+ t3 = 250 # high temperature in Kelvin (250 degC)
+ r3 = 226.15 # resistance at high temperature (226.15 Ohm)
+ rp = 4700; # pull-up resistor (4.7 kOhm)
+ num_temps = 36; # number of entries for look-up table
+
+ try:
+ opts, args = getopt.getopt(argv, "h", ["help", "rp=", "t1=", "t2=", "t3=", "num-temps="])
+ except getopt.GetoptError as err:
+ print(str(err))
+ usage()
+ sys.exit(2)
+
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ usage()
+ sys.exit()
+ elif opt == "--rp":
+ rp = int(arg)
+ elif opt == "--t1":
+ arg = arg.split(':')
+ t1 = float(arg[0])
+ r1 = float(arg[1])
+ elif opt == "--t2":
+ arg = arg.split(':')
+ t2 = float(arg[0])
+ r2 = float(arg[1])
+ elif opt == "--t3":
+ arg = arg.split(':')
+ t3 = float(arg[0])
+ r3 = float(arg[1])
+ elif opt == "--num-temps":
+ num_temps = int(arg)
+
+ t = Thermistor(rp, t1, r1, t2, r2, t3, r3)
+ increment = int((ARES-1)/(num_temps-1));
+ step = (TMIN-TMAX) / (num_temps-1)
+ low_bound = t.temp(ARES-1);
+ up_bound = t.temp(1);
+ min_temp = int(TMIN if TMIN > low_bound else low_bound)
+ max_temp = int(TMAX if TMAX < up_bound else up_bound)
+ temps = list(range(max_temp, TMIN+step, step));
+
+ print("// Thermistor lookup table for Marlin")
+ print("// ./createTemperatureLookupMarlin.py --rp=%s --t1=%s:%s --t2=%s:%s --t3=%s:%s --num-temps=%s" % (rp, t1, r1, t2, r2, t3, r3, num_temps))
+ print("// Steinhart-Hart Coefficients: a=%.15g, b=%.15g, c=%.15g " % (t.c1, t.c2, t.c3))
+ print("// Theoretical limits of thermistor: %.2f to %.2f degC" % (low_bound, up_bound))
+ print()
+ print("const short temptable[][2] PROGMEM = {")
+
+ for temp in temps:
+ adc = t.adc(temp)
+ print(" { OV(%7.2f), %4s }%s // v=%.3f\tr=%.3f\tres=%.3f degC/count" % (adc , temp, \
+ ',' if temp != temps[-1] else ' ', \
+ t.voltage(adc), \
+ t.resist( adc), \
+ t.resol( adc) \
+ ))
+ print("};")
+
+def usage():
+ print(__doc__)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/buildroot/share/scripts/findMissingTranslations.sh b/buildroot/share/scripts/findMissingTranslations.sh
new file mode 100644
index 0000000..0cf7736
--- /dev/null
+++ b/buildroot/share/scripts/findMissingTranslations.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+#
+# findMissingTranslations.sh
+#
+# Locate all language strings needing an update based on English
+#
+# Usage: findMissingTranslations.sh [language codes]
+#
+# If no language codes are specified then all languages will be checked
+#
+
+LANGHOME="Marlin/src/lcd/language"
+
+[ -d $LANGHOME ] && cd $LANGHOME
+
+FILES=$(ls language_*.h | grep -v -E "(_en|_test)\.h" | sed -E 's/language_([^\.]+)\.h/\1/')
+declare -A STRING_MAP
+
+# Get files matching the given arguments
+TEST_LANGS=$FILES
+if [[ -n $@ ]]; then
+ TEST_LANGS=""
+ for K in "$@"; do
+ for F in $FILES; do
+ [[ "$F" != "${F%$K*}" ]] && TEST_LANGS="$TEST_LANGS $F"
+ done
+ done
+fi
+
+echo -n "Building list of missing strings..."
+
+for i in $(awk '/Language_Str/{print $3}' language_en.h); do
+ [[ $i == "MSG_CUBED" ]] && continue
+ LANG_LIST=""
+ for j in $TEST_LANGS; do
+ [[ $(grep -c " ${i} " language_${j}.h) -eq 0 ]] && LANG_LIST="$LANG_LIST $j"
+ done
+ [[ -z $LANG_LIST ]] && continue
+ STRING_MAP[$i]=$LANG_LIST
+done
+
+echo
+
+for K in $( printf "%s\n" "${!STRING_MAP[@]}" | sort ); do
+ case "$#" in
+ 1 ) echo $K ;;
+ * ) printf "%-35s :%s\n" "$K" "${STRING_MAP[$K]}" ;;
+ esac
+done
diff --git a/buildroot/share/scripts/g29_auto.py b/buildroot/share/scripts/g29_auto.py
new file mode 100644
index 0000000..ffcb0d9
--- /dev/null
+++ b/buildroot/share/scripts/g29_auto.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+
+# This file is for preprocessing gcode and the new G29 Autobedleveling from Marlin
+# It will analyse the first 2 Layer and return the maximum size for this part
+# After this it will replace with g29_keyword = ';MarlinG29Script' with the new G29 LRFB
+# the new file will be created in the same folder.
+
+from __future__ import print_function
+
+# your gcode-file/folder
+folder = './'
+my_file = 'test.gcode'
+
+# this is the minimum of G1 instructions which should be between 2 different heights
+min_g1 = 3
+
+# maximum number of lines to parse, I don't want to parse the complete file
+# only the first plane is we are interested in
+max_g1 = 100000000
+
+# g29 keyword
+g29_keyword = 'g29'
+g29_keyword = g29_keyword.upper()
+
+# output filename
+output_file = folder + 'g29_' + my_file
+# input filename
+input_file = folder + my_file
+
+# minimum scan size
+min_size = 40
+probing_points = 3 # points x points
+
+# other stuff
+min_x = 500
+min_y = min_x
+max_x = -500
+max_y = max_x
+last_z = 0.001
+
+layer = 0
+lines_of_g1 = 0
+
+gcode = []
+
+
+# return only g1-lines
+def has_g1(line):
+ return line[:2].upper() == "G1"
+
+
+# find position in g1 (x,y,z)
+def find_axis(line, axis):
+ found = False
+ number = ""
+ for char in line:
+ if found:
+ if char == ".":
+ number += char
+ elif char == "-":
+ number += char
+ else:
+ try:
+ int(char)
+ number += char
+ except ValueError:
+ break
+ else:
+ found = char.upper() == axis.upper()
+ try:
+ return float(number)
+ except ValueError:
+ return None
+
+
+# save the min or max-values for each axis
+def set_mima(line):
+ global min_x, max_x, min_y, max_y, last_z
+
+ current_x = find_axis(line, 'x')
+ current_y = find_axis(line, 'y')
+
+ if current_x is not None:
+ min_x = min(current_x, min_x)
+ max_x = max(current_x, max_x)
+ if current_y is not None:
+ min_y = min(current_y, min_y)
+ max_y = max(current_y, max_y)
+
+ return min_x, max_x, min_y, max_y
+
+
+# find z in the code and return it
+def find_z(gcode, start_at_line=0):
+ for i in range(start_at_line, len(gcode)):
+ my_z = find_axis(gcode[i], 'Z')
+ if my_z is not None:
+ return my_z, i
+
+
+def z_parse(gcode, start_at_line=0, end_at_line=0):
+ i = start_at_line
+ all_z = []
+ line_between_z = []
+ z_at_line = []
+ # last_z = 0
+ last_i = -1
+
+ while len(gcode) > i:
+ try:
+ z, i = find_z(gcode, i + 1)
+ except TypeError:
+ break
+
+ all_z.append(z)
+ z_at_line.append(i)
+ temp_line = i - last_i -1
+ line_between_z.append(i - last_i - 1)
+ # last_z = z
+ last_i = i
+ if 0 < end_at_line <= i or temp_line >= min_g1:
+ # print('break at line {} at heigth {}'.format(i, z))
+ break
+
+ line_between_z = line_between_z[1:]
+ return all_z, line_between_z, z_at_line
+
+
+# get the lines which should be the first layer
+def get_lines(gcode, minimum):
+ i = 0
+ all_z, line_between_z, z_at_line = z_parse(gcode, end_at_line=max_g1)
+ for count in line_between_z:
+ i += 1
+ if count > minimum:
+ # print('layer: {}:{}'.format(z_at_line[i-1], z_at_line[i]))
+ return z_at_line[i - 1], z_at_line[i]
+
+
+with open(input_file, 'r') as file:
+ lines = 0
+ for line in file:
+ lines += 1
+ if lines > 1000:
+ break
+ if has_g1(line):
+ gcode.append(line)
+file.close()
+
+start, end = get_lines(gcode, min_g1)
+for i in range(start, end):
+ set_mima(gcode[i])
+
+print('x_min:{} x_max:{}\ny_min:{} y_max:{}'.format(min_x, max_x, min_y, max_y))
+
+# resize min/max - values for minimum scan
+if max_x - min_x < min_size:
+ offset_x = int((min_size - (max_x - min_x)) / 2 + 0.5) # int round up
+ # print('min_x! with {}'.format(int(max_x - min_x)))
+ min_x = int(min_x) - offset_x
+ max_x = int(max_x) + offset_x
+if max_y - min_y < min_size:
+ offset_y = int((min_size - (max_y - min_y)) / 2 + 0.5) # int round up
+ # print('min_y! with {}'.format(int(max_y - min_y)))
+ min_y = int(min_y) - offset_y
+ max_y = int(max_y) + offset_y
+
+
+new_command = 'G29 L{0} R{1} F{2} B{3} P{4}\n'.format(min_x,
+ max_x,
+ min_y,
+ max_y,
+ probing_points)
+
+out_file = open(output_file, 'w')
+in_file = open(input_file, 'r')
+
+for line in in_file:
+ if line[:len(g29_keyword)].upper() == g29_keyword:
+ out_file.write(new_command)
+ print('write G29')
+ else:
+ out_file.write(line)
+
+file.close()
+out_file.close()
+
+print('auto G29 finished')
diff --git a/buildroot/share/scripts/pinsformat.js b/buildroot/share/scripts/pinsformat.js
new file mode 100644
index 0000000..a82c2f2
--- /dev/null
+++ b/buildroot/share/scripts/pinsformat.js
@@ -0,0 +1,170 @@
+#!/usr/bin/env node
+
+//
+// Formatter script for pins_MYPINS.h files
+//
+// Usage: mffmt [infile] [outfile]
+//
+// With no parameters convert STDIN to STDOUT
+//
+
+const fs = require("fs");
+
+// String lpad / rpad
+String.prototype.lpad = function(len, chr) {
+ if (!len) return this;
+ if (chr === undefined) chr = ' ';
+ var s = this+'', need = len - s.length;
+ if (need > 0) s = new Array(need+1).join(chr) + s;
+ return s;
+};
+
+String.prototype.rpad = function(len, chr) {
+ if (!len) return this;
+ if (chr === undefined) chr = ' ';
+ var s = this+'', need = len - s.length;
+ if (need > 0) s += new Array(need+1).join(chr);
+ return s;
+};
+
+const mpatt = [ '-?\\d+', 'P[A-I]\\d+', 'P\\d_\\d+' ],
+ definePatt = new RegExp(`^\\s*(//)?#define\\s+[A-Z_][A-Z0-9_]+\\s+(${mpatt[0]}|${mpatt[1]}|${mpatt[2]})\\s*(//.*)?$`, 'gm'),
+ ppad = [ 3, 4, 5 ],
+ col_comment = 50,
+ col_value_rj = col_comment - 3;
+
+var mexpr = [];
+for (let m of mpatt) mexpr.push(new RegExp('^' + m + '$'));
+
+const argv = process.argv.slice(2), argc = argv.length;
+
+var src_file = 0, src_name = 'STDIN', dst_file, do_log = false;
+if (argc > 0) {
+ let ind = 0;
+ if (argv[0] == '-v') { do_log = true; ind++; }
+ dst_file = src_file = src_name = argv[ind++];
+ if (ind < argc) dst_file = argv[ind];
+}
+
+// Read from file or STDIN until it terminates
+const filtered = process_text(fs.readFileSync(src_file).toString());
+if (dst_file)
+ fs.writeFileSync(dst_file, filtered);
+else
+ console.log(filtered);
+
+// Find the pin pattern so non-pin defines can be skipped
+function get_pin_pattern(txt) {
+ var r, m = 0, match_count = [ 0, 0, 0 ];
+ definePatt.lastIndex = 0;
+ while ((r = definePatt.exec(txt)) !== null) {
+ let ind = -1;
+ if (mexpr.some((p) => {
+ ind++;
+ const didmatch = r[2].match(p);
+ return r[2].match(p);
+ }) ) {
+ const m = ++match_count[ind];
+ if (m >= 10) {
+ return { match: mpatt[ind], pad:ppad[ind] };
+ }
+ }
+ }
+ return null;
+}
+
+function process_text(txt) {
+ if (!txt.length) return '(no text)';
+ const patt = get_pin_pattern(txt);
+ if (!patt) return txt;
+ const pindefPatt = new RegExp(`^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+(${patt.match})\\s*(//.*)?$`),
+ noPinPatt = new RegExp(`^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+(-1)\\s*(//.*)?$`),
+ skipPatt = new RegExp('^(\\s*(//)?#define)\\s+(AT90USB|USBCON|BOARD_.+|.+_MACHINE_NAME|.+_SERIAL)\\s+(.+)\\s*(//.*)?$'),
+ aliasPatt = new RegExp('^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+([A-Z_][A-Z0-9_()]+)\\s*(//.*)?$'),
+ switchPatt = new RegExp('^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s*(//.*)?$'),
+ undefPatt = new RegExp('^(\\s*(//)?#undef)\\s+([A-Z_][A-Z0-9_]+)\\s*(//.*)?$'),
+ defPatt = new RegExp('^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+([-_\\w]+)\\s*(//.*)?$'),
+ condPatt = new RegExp('^(\\s*(//)?#(if|ifn?def|else|elif)(\\s+\\S+)*)\\s+(//.*)$'),
+ commPatt = new RegExp('^\\s{20,}(//.*)?$');
+ const col_value_lj = col_comment - patt.pad - 2;
+ var r, out = '', check_comment_next = false;
+ txt.split('\n').forEach((line) => {
+ if (check_comment_next)
+ check_comment_next = ((r = commPatt.exec(line)) !== null);
+
+ if (check_comment_next)
+ // Comments in column 45
+ line = ''.rpad(col_comment) + r[1];
+
+ else if ((r = pindefPatt.exec(line)) !== null) {
+ //
+ // #define MY_PIN [pin]
+ //
+ if (do_log) console.log("pin:", line);
+ const pinnum = r[4].charAt(0) == 'P' ? r[4] : r[4].lpad(patt.pad);
+ line = r[1] + ' ' + r[3];
+ line = line.rpad(col_value_lj) + pinnum;
+ if (r[5]) line = line.rpad(col_comment) + r[5];
+ }
+ else if ((r = noPinPatt.exec(line)) !== null) {
+ //
+ // #define MY_PIN -1
+ //
+ if (do_log) console.log("pin -1:", line);
+ line = r[1] + ' ' + r[3];
+ line = line.rpad(col_value_lj) + '-1';
+ if (r[5]) line = line.rpad(col_comment) + r[5];
+ }
+ else if ((r = skipPatt.exec(line)) !== null) {
+ //
+ // #define SKIP_ME
+ //
+ if (do_log) console.log("skip:", line);
+ }
+ else if ((r = aliasPatt.exec(line)) !== null) {
+ //
+ // #define ALIAS OTHER
+ //
+ if (do_log) console.log("alias:", line);
+ line = r[1] + ' ' + r[3];
+ line += r[4].lpad(col_value_rj + 1 - line.length);
+ if (r[5]) line = line.rpad(col_comment) + r[5];
+ }
+ else if ((r = switchPatt.exec(line)) !== null) {
+ //
+ // #define SWITCH
+ //
+ if (do_log) console.log("switch:", line);
+ line = r[1] + ' ' + r[3];
+ if (r[4]) line = line.rpad(col_comment) + r[4];
+ check_comment_next = true;
+ }
+ else if ((r = defPatt.exec(line)) !== null) {
+ //
+ // #define ...
+ //
+ if (do_log) console.log("def:", line);
+ line = r[1] + ' ' + r[3] + ' ';
+ line += r[4].lpad(col_value_rj + 1 - line.length);
+ if (r[5]) line = line.rpad(col_comment - 1) + ' ' + r[5];
+ }
+ else if ((r = undefPatt.exec(line)) !== null) {
+ //
+ // #undef ...
+ //
+ if (do_log) console.log("undef:", line);
+ line = r[1] + ' ' + r[3];
+ if (r[4]) line = line.rpad(col_comment) + r[4];
+ }
+ else if ((r = condPatt.exec(line)) !== null) {
+ //
+ // #if ...
+ //
+ if (do_log) console.log("cond:", line);
+ line = r[1].rpad(col_comment) + r[5];
+ check_comment_next = true;
+ }
+ out += line + '\n';
+ });
+ return out.replace(/\n\n+/g, '\n\n').replace(/\n\n$/g, '\n');
+}