Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cproject26
-rw-r--r--.settings/org.eclipse.cdt.managedbuilder.core.prefs12
-rw-r--r--Developer-documentation/OpenAPI.yaml577
-rw-r--r--src/Accelerometers/Accelerometers.cpp2
-rw-r--r--src/Accelerometers/LIS3DH.h2
-rw-r--r--src/CAN/CanInterface.cpp11
-rw-r--r--src/Comms/PanelDueUpdater.cpp4
-rw-r--r--src/Config/Configuration.h15
-rw-r--r--src/Config/Pins.h24
-rw-r--r--src/Config/Pins_Duet3Mini.h16
-rw-r--r--src/Config/Pins_Duet3_MB6HC.h5
-rw-r--r--src/Config/Pins_Duet3_MB6XD.h5
-rw-r--r--src/Config/Pins_DuetM.h5
-rw-r--r--src/Config/Pins_DuetNG.h11
-rw-r--r--src/Config/Pins_FMDC.h24
-rw-r--r--src/Config/Pins_Pccb.h1
-rw-r--r--src/Display/ButtonMenuItem.cpp97
-rw-r--r--src/Display/ButtonMenuItem.h38
-rw-r--r--src/Display/Display.cpp266
-rw-r--r--src/Display/Display.h29
-rw-r--r--src/Display/DisplayOrientation.h26
-rw-r--r--src/Display/FilesMenuItem.cpp410
-rw-r--r--src/Display/FilesMenuItem.h63
-rw-r--r--src/Display/ImageMenuItem.cpp78
-rw-r--r--src/Display/ImageMenuItem.h31
-rw-r--r--src/Display/Lcd/Fonts/ER3301_1.cpp199
-rw-r--r--src/Display/Lcd/Fonts/ER3301_1.h62
-rw-r--r--src/Display/Lcd/Fonts/glcd11x14.cpp2
-rw-r--r--src/Display/Lcd/Fonts/glcd19x21.cpp549
-rw-r--r--src/Display/Lcd/Fonts/glcd28x32.cpp549
-rw-r--r--src/Display/Lcd/Fonts/glcd7x11.cpp2
-rw-r--r--src/Display/Lcd/ILI9488/ILI9488.cpp235
-rw-r--r--src/Display/Lcd/ILI9488/ILI9488.h121
-rw-r--r--src/Display/Lcd/Lcd.cpp484
-rw-r--r--src/Display/Lcd/Lcd.h191
-rw-r--r--src/Display/Lcd/MonoLcd.cpp241
-rw-r--r--src/Display/Lcd/MonoLcd.h88
-rw-r--r--src/Display/Lcd/ST7567/Lcd7567.cpp4
-rw-r--r--src/Display/Lcd/ST7567/Lcd7567.h6
-rw-r--r--src/Display/Lcd/ST7920/Lcd7920.cpp6
-rw-r--r--src/Display/Lcd/ST7920/Lcd7920.h6
-rw-r--r--src/Display/Lcd/TFTLcd.cpp49
-rw-r--r--src/Display/Lcd/TFTLcd.h58
-rw-r--r--src/Display/Menu.cpp273
-rw-r--r--src/Display/Menu.h14
-rw-r--r--src/Display/MenuItem.cpp1055
-rw-r--r--src/Display/MenuItem.h175
-rw-r--r--src/Display/ResistiveTouch.cpp182
-rw-r--r--src/Display/ResistiveTouch.h46
-rw-r--r--src/Display/RotaryEncoder.cpp2
-rw-r--r--src/Display/RotaryEncoder.h2
-rw-r--r--src/Display/TextMenuItem.cpp57
-rw-r--r--src/Display/TextMenuItem.h33
-rw-r--r--src/Display/ValueMenuItem.cpp399
-rw-r--r--src/Display/ValueMenuItem.h59
-rw-r--r--src/Endstops/EndstopsManager.cpp104
-rw-r--r--src/Endstops/EndstopsManager.h14
-rw-r--r--src/Endstops/ZProbe.cpp76
-rw-r--r--src/Endstops/ZProbe.h6
-rw-r--r--src/Fans/Fan.cpp5
-rw-r--r--src/Fans/FansManager.cpp3
-rw-r--r--src/GCodes/CollisionAvoider.cpp64
-rw-r--r--src/GCodes/CollisionAvoider.h46
-rw-r--r--src/GCodes/GCodeBuffer/BinaryParser.cpp26
-rw-r--r--src/GCodes/GCodeBuffer/BinaryParser.h1
-rw-r--r--src/GCodes/GCodeBuffer/ExpressionParser.cpp325
-rw-r--r--src/GCodes/GCodeBuffer/ExpressionParser.h5
-rw-r--r--src/GCodes/GCodeBuffer/GCodeBuffer.cpp270
-rw-r--r--src/GCodes/GCodeBuffer/GCodeBuffer.h75
-rw-r--r--src/GCodes/GCodeBuffer/StringParser.cpp35
-rw-r--r--src/GCodes/GCodeBuffer/StringParser.h3
-rw-r--r--src/GCodes/GCodeChannel.h2
-rw-r--r--src/GCodes/GCodeException.cpp14
-rw-r--r--src/GCodes/GCodeException.h19
-rw-r--r--src/GCodes/GCodeMachineState.cpp38
-rw-r--r--src/GCodes/GCodeMachineState.h30
-rw-r--r--src/GCodes/GCodeQueue.cpp132
-rw-r--r--src/GCodes/GCodeQueue.h9
-rw-r--r--src/GCodes/GCodes.cpp2396
-rw-r--r--src/GCodes/GCodes.h305
-rw-r--r--src/GCodes/GCodes2.cpp1114
-rw-r--r--src/GCodes/GCodes3.cpp459
-rw-r--r--src/GCodes/GCodes4.cpp347
-rw-r--r--src/GCodes/GCodes5.cpp356
-rw-r--r--src/GCodes/GCodes6.cpp458
-rw-r--r--src/GCodes/GCodes7.cpp163
-rw-r--r--src/GCodes/ObjectTracker.cpp343
-rw-r--r--src/GCodes/ObjectTracker.h51
-rw-r--r--src/GCodes/RestorePoint.cpp17
-rw-r--r--src/GCodes/RestorePoint.h3
-rw-r--r--src/Hardware/ExceptionHandlers.cpp10
-rw-r--r--src/Hardware/SAM4E/sam4e8e_flash.ld3
-rw-r--r--src/Hardware/SAME5x/Devices.h2
-rw-r--r--src/Hardware/SharedSpi/SharedSpiDevice.h54
-rw-r--r--src/Hardware/Spi/SharedSpiClient.cpp (renamed from src/Hardware/SharedSpi/SharedSpiClient.cpp)6
-rw-r--r--src/Hardware/Spi/SharedSpiClient.h (renamed from src/Hardware/SharedSpi/SharedSpiClient.h)6
-rw-r--r--src/Hardware/Spi/SharedSpiDevice.cpp47
-rw-r--r--src/Hardware/Spi/SharedSpiDevice.h33
-rw-r--r--src/Hardware/Spi/SpiDevice.cpp (renamed from src/Hardware/SharedSpi/SharedSpiDevice.cpp)198
-rw-r--r--src/Hardware/Spi/SpiDevice.h68
-rw-r--r--src/Hardware/Spi/SpiMode.h (renamed from src/Hardware/SharedSpi/SpiMode.h)6
-rw-r--r--src/Heating/Heat.cpp70
-rw-r--r--src/Heating/Heat.h5
-rw-r--r--src/Heating/Heater.cpp120
-rw-r--r--src/Heating/Heater.h15
-rw-r--r--src/Heating/HeaterMonitor.cpp4
-rw-r--r--src/Heating/HeaterMonitor.h2
-rw-r--r--src/Heating/LocalHeater.cpp6
-rw-r--r--src/Heating/RemoteHeater.cpp2
-rw-r--r--src/Heating/Sensors/AdditionalOutputSensor.cpp127
-rw-r--r--src/Heating/Sensors/AdditionalOutputSensor.h7
-rw-r--r--src/Heating/Sensors/BME280.cpp788
-rw-r--r--src/Heating/Sensors/BME280.h94
-rw-r--r--src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp2
-rw-r--r--src/Heating/Sensors/DhtSensor.cpp7
-rw-r--r--src/Heating/Sensors/DhtSensor.h7
-rw-r--r--src/Heating/Sensors/RtdSensor31865.cpp7
-rw-r--r--src/Heating/Sensors/SpiTemperatureSensor.cpp29
-rw-r--r--src/Heating/Sensors/SpiTemperatureSensor.h6
-rw-r--r--src/Heating/Sensors/TemperatureSensor.cpp20
-rw-r--r--src/Heating/Sensors/TemperatureSensor.h3
-rw-r--r--src/Heating/Sensors/ThermocoupleSensor31855.cpp5
-rw-r--r--src/Heating/Sensors/ThermocoupleSensor31856.cpp6
-rw-r--r--src/Heating/Sensors/bme280_defs.h482
-rw-r--r--src/Libraries/Fatfs/ff.h10
-rw-r--r--src/Libraries/sd_mmc/sd_mmc_spi.cpp4
-rw-r--r--src/Movement/AxisShaper.cpp34
-rw-r--r--src/Movement/AxisShaper.h4
-rw-r--r--src/Movement/BedProbing/Grid.cpp214
-rw-r--r--src/Movement/BedProbing/Grid.h45
-rw-r--r--src/Movement/DDA.cpp30
-rw-r--r--src/Movement/DDA.h2
-rw-r--r--src/Movement/DDARing.cpp23
-rw-r--r--src/Movement/DDARing.h7
-rw-r--r--src/Movement/HeightControl/HeightController.cpp14
-rw-r--r--src/Movement/HeightControl/HeightController.h2
-rw-r--r--src/Movement/Kinematics/CoreKinematics.cpp58
-rw-r--r--src/Movement/Kinematics/CoreKinematics.h6
-rw-r--r--src/Movement/Kinematics/FiveBarScaraKinematics.cpp20
-rw-r--r--src/Movement/Kinematics/HangprinterKinematics.cpp632
-rw-r--r--src/Movement/Kinematics/HangprinterKinematics.h69
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.cpp19
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.h3
-rw-r--r--src/Movement/Kinematics/PolarKinematics.cpp2
-rw-r--r--src/Movement/Kinematics/RotaryDeltaKinematics.cpp29
-rw-r--r--src/Movement/Kinematics/ScaraKinematics.cpp18
-rw-r--r--src/Movement/Kinematics/ZLeadscrewKinematics.cpp49
-rw-r--r--src/Movement/Kinematics/ZLeadscrewKinematics.h5
-rw-r--r--src/Movement/Move.cpp272
-rw-r--r--src/Movement/Move.h89
-rw-r--r--src/Movement/RawMove.cpp194
-rw-r--r--src/Movement/RawMove.h97
-rw-r--r--src/Movement/StepperDrivers/TMC22xx.cpp422
-rw-r--r--src/Movement/StepperDrivers/TMC22xx.h2
-rw-r--r--src/Networking/ESP8266WiFi/WiFiInterface.cpp282
-rw-r--r--src/Networking/ESP8266WiFi/WifiFirmwareUploader.cpp185
-rw-r--r--src/Networking/ESP8266WiFi/WifiFirmwareUploader.h23
-rw-r--r--src/Networking/Network.cpp20
-rw-r--r--src/Networking/Network.h3
-rw-r--r--src/ObjectModel/GlobalVariables.cpp55
-rw-r--r--src/ObjectModel/GlobalVariables.h2
-rw-r--r--src/ObjectModel/ObjectModel.cpp588
-rw-r--r--src/ObjectModel/ObjectModel.h165
-rw-r--r--src/ObjectModel/TypeCode.h46
-rw-r--r--src/ObjectModel/Variable.cpp47
-rw-r--r--src/ObjectModel/Variable.h14
-rw-r--r--src/Platform/ArrayHandle.cpp152
-rw-r--r--src/Platform/ArrayHandle.h50
-rw-r--r--src/Platform/Heap.cpp296
-rw-r--r--src/Platform/Heap.h92
-rw-r--r--src/Platform/MessageBox.cpp140
-rw-r--r--src/Platform/MessageBox.h77
-rw-r--r--src/Platform/Platform.cpp116
-rw-r--r--src/Platform/Platform.h15
-rw-r--r--src/Platform/RepRap.cpp873
-rw-r--r--src/Platform/RepRap.h106
-rw-r--r--src/Platform/Roland.cpp267
-rw-r--r--src/Platform/Roland.h74
-rw-r--r--src/Platform/StringHandle.cpp153
-rw-r--r--src/Platform/StringHandle.h54
-rw-r--r--src/PrintMonitor/PrintMonitor.cpp64
-rw-r--r--src/PrintMonitor/PrintMonitor.h4
-rw-r--r--src/RepRapFirmware.h13
-rw-r--r--src/SBC/SbcInterface.cpp2
-rw-r--r--src/Storage/FileData.h4
-rw-r--r--src/Storage/FileInfoParser.cpp2
-rw-r--r--src/Storage/FileStore.cpp18
-rw-r--r--src/Storage/FileStore.h3
-rw-r--r--src/Storage/MassStorage.cpp31
-rw-r--r--src/Storage/MassStorage.h4
-rw-r--r--src/Tools/Filament.cpp9
-rw-r--r--src/Tools/Filament.h10
-rw-r--r--src/Tools/Spindle.cpp42
-rw-r--r--src/Tools/Spindle.h1
-rw-r--r--src/Tools/Tool.cpp370
-rw-r--r--src/Tools/Tool.h58
-rw-r--r--src/Version.h2
-rw-r--r--src/bossa/BossaFlash.cpp29
-rw-r--r--src/bossa/BossaFlash.h49
-rw-r--r--src/bossa/Device.cpp12
-rw-r--r--src/bossa/Device.h2
-rw-r--r--src/bossa/EefcFlash.cpp97
-rw-r--r--src/bossa/EefcFlash.h16
-rw-r--r--src/bossa/Flasher.cpp2
-rw-r--r--src/bossa/SerialPort.h4
-rw-r--r--src/libc/errno.c17
-rw-r--r--src/libc/nano-mallocr.c11
207 files changed, 15958 insertions, 7676 deletions
diff --git a/.cproject b/.cproject
index ec0fce31..550b2484 100644
--- a/.cproject
+++ b/.cproject
@@ -19,7 +19,7 @@
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
- <configuration artifactExtension="elf" artifactName="Duet2CombinedFirmware" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622" name="Duet2" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.enablement=null,org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.image=null,org.eclipse.cdt.docker.launcher.containerbuild.property.connection=null" parent="cdt.managedbuild.config.gnu.cross.exe.release" postannouncebuildStep="Generating binary file" postbuildStep="arm-none-eabi-objcopy -O binary &quot;${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.elf&quot; &quot;${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.bin&quot; &amp;&amp; crc32appender &quot;${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.bin&quot;">
+ <configuration artifactExtension="elf" artifactName="Duet2CombinedFirmware" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622" name="Duet2" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.enablement=null,org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.image=null,org.eclipse.cdt.docker.launcher.containerbuild.property.connection=null" parent="cdt.managedbuild.config.gnu.cross.exe.release" postannouncebuildStep="Generating binary file" postbuildStep="arm-none-eabi-objcopy -O binary &quot;${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.elf&quot; &quot;${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.bin&quot; &amp;&amp; crcappender &quot;${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.bin&quot;">
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622." name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.exe.release.435431950" name="Cross GCC" nonInternalBuilderId="cdt.managedbuild.builder.gnu.cross" superClass="cdt.managedbuild.toolchain.gnu.cross.exe.release">
<option id="cdt.managedbuild.option.gnu.cross.path.1881231799" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path" useByScannerDiscovery="false" value="${ArmGccPath}" valueType="string"/>
@@ -38,7 +38,7 @@
<option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.option.optimization.level.456685708" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.c.optimization.level.size" valueType="enumerated"/>
<option id="gnu.c.compiler.option.debugging.level.1579494397" name="Debug Level" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.none" valueType="enumerated"/>
<option id="gnu.c.compiler.option.misc.verbose.328543251" name="Verbose (-v)" superClass="gnu.c.compiler.option.misc.verbose" useByScannerDiscovery="false" value="false" valueType="boolean"/>
- <option id="gnu.c.compiler.option.misc.other.465279131" name="Other flags" superClass="gnu.c.compiler.option.misc.other" useByScannerDiscovery="true" value="-c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee -ffunction-sections -fdata-sections -nostdlib -Wundef -Wdouble-promotion -Werror=return-type -Werror=implicit -fsingle-precision-constant &quot;-Wa,-ahl=$*.s&quot;" valueType="string"/>
+ <option id="gnu.c.compiler.option.misc.other.465279131" name="Other flags" superClass="gnu.c.compiler.option.misc.other" useByScannerDiscovery="true" value="-c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee -ffunction-sections -fdata-sections -nostdlib -Wundef -Wdouble-promotion -Werror=return-type -Werror=implicit -fsingle-precision-constant" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.include.paths.288982451" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/CANlib}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/CoreN2G}&quot;"/>
@@ -63,6 +63,7 @@
</option>
<option id="gnu.c.compiler.option.dialect.std.1370424675" name="Language standard" superClass="gnu.c.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.c.compiler.dialect.default" valueType="enumerated"/>
<option id="gnu.c.compiler.option.dialect.flags.1078349416" name="Other dialect flags" superClass="gnu.c.compiler.option.dialect.flags" useByScannerDiscovery="true" value="-std=gnu99" valueType="string"/>
+ <option id="gnu.c.compiler.option.optimization.flags.2118944913" name="Other optimization flags" superClass="gnu.c.compiler.option.optimization.flags" useByScannerDiscovery="false" value="" valueType="string"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1065384487" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1102044150" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
@@ -92,7 +93,7 @@
<option id="gnu.cpp.compiler.option.optimization.level.1835678360" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.size" valueType="enumerated"/>
<option id="gnu.cpp.compiler.option.debugging.level.737051102" name="Debug Level" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
<option id="gnu.cpp.compiler.option.other.verbose.1225557122" name="Verbose (-v)" superClass="gnu.cpp.compiler.option.other.verbose" useByScannerDiscovery="false" value="false" valueType="boolean"/>
- <option id="gnu.cpp.compiler.option.other.other.1423466590" name="Other flags" superClass="gnu.cpp.compiler.option.other.other" useByScannerDiscovery="true" value="-c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee -ffunction-sections -fdata-sections -fno-threadsafe-statics -fno-rtti -fexceptions -nostdlib -Wundef -Wdouble-promotion -Werror=return-type -Wsuggest-override -fsingle-precision-constant &quot;-Wa,-ahl=$*.s&quot; -fstack-usage" valueType="string"/>
+ <option id="gnu.cpp.compiler.option.other.other.1423466590" name="Other flags" superClass="gnu.cpp.compiler.option.other.other" useByScannerDiscovery="true" value="-c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee -ffunction-sections -fdata-sections -fno-threadsafe-statics -fno-rtti -fexceptions -nostdlib -Wundef -Wdouble-promotion -Werror=return-type -Wsuggest-override -fsingle-precision-constant -fstack-usage" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.cpp.compiler.option.include.paths.1505018967" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/CANlib}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/CoreN2G}&quot;"/>
@@ -125,6 +126,7 @@
</option>
<option id="gnu.cpp.compiler.option.dialect.std.1493465864" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.cpp.compiler.dialect.default" valueType="enumerated"/>
<option id="gnu.cpp.compiler.option.dialect.flags.316329964" name="Other dialect flags" superClass="gnu.cpp.compiler.option.dialect.flags" useByScannerDiscovery="true" value="-std=gnu++17" valueType="string"/>
+ <option id="gnu.cpp.compiler.option.optimization.flags.1174285047" name="Other optimization flags" superClass="gnu.cpp.compiler.option.optimization.flags" useByScannerDiscovery="false" value="" valueType="string"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.65828751" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
</toolChain>
@@ -1883,7 +1885,7 @@
</toolChain>
</folderInfo>
<sourceEntries>
- <entry excluding="src/Hardware/SAME5x/Ethernet|src/Networking/LwipEthernet|src/Networking/LwipEthernet/Lwip/src/apps/snmp|src/Networking/LwipEthernet/Lwip/src/apps/smtp|src/Hardware/SAME70|src/DuetNG|src/Networking/LwipEthernet/Lwip/src/apps/tftp|src/Networking/W5500Ethernet|src/Networking/LwipEthernet/Lwip/src/netif/ppp|src/Networking/LwipEthernet/Lwip/src/apps/lwiperf|src/Networking/LwipEthernet/Lwip/src/apps/altcp_tls|src/Networking/LwipEthernet/Lwip/src/apps/sntp|src/Networking/LwipEthernet/Lwip/src/apps/http|src/Duet3_V06|src/Hardware/SAM4E|src/Pccb|src/Hardware/SAM4S|src/Networking/LwipEthernet/Lwip/src/apps/mqtt|src/DuetM|src/Networking/LwipEthernet/Lwip/doc" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
+ <entry excluding="src/Hardware/SAME5x/Ethernet|src/Networking/LwipEthernet/Lwip/src/apps/snmp|src/Networking/LwipEthernet/Lwip/src/apps/smtp|src/Hardware/SAME70|src/Networking/LwipEthernet|src/DuetNG|src/Networking/LwipEthernet/Lwip/src/apps/tftp|src/Networking/W5500Ethernet|src/Networking/LwipEthernet/Lwip/src/netif/ppp|src/Networking/LwipEthernet/Lwip/src/apps/lwiperf|src/Networking/LwipEthernet/Lwip/src/apps/altcp_tls|src/Networking/LwipEthernet/Lwip/src/apps/sntp|src/Networking/LwipEthernet/Lwip/src/apps/http|src/Duet3_V06|src/Hardware/SAM4E|src/Pccb|src/Hardware/SAM4S|src/Networking/LwipEthernet/Lwip/src/apps/mqtt|src/DuetM|src/Networking/LwipEthernet/Lwip/doc" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>
@@ -2034,7 +2036,7 @@
</toolChain>
</folderInfo>
<sourceEntries>
- <entry excluding="src/Hardware/SAME5x/Ethernet|src/Networking/LwipEthernet|src/Networking/LwipEthernet/Lwip/src/apps/snmp|src/Networking/LwipEthernet/Lwip/src/apps/smtp|src/Hardware/SAME70|src/DuetNG|src/Networking/LwipEthernet/Lwip/src/apps/tftp|src/Networking/W5500Ethernet|src/Networking/LwipEthernet/Lwip/src/netif/ppp|src/Networking/LwipEthernet/Lwip/src/apps/lwiperf|src/Networking/LwipEthernet/Lwip/src/apps/altcp_tls|src/Networking/LwipEthernet/Lwip/src/apps/sntp|src/Networking/LwipEthernet/Lwip/src/apps/http|src/Duet3_V06|src/Hardware/SAM4E|src/Pccb|src/Hardware/SAM4S|src/Networking/LwipEthernet/Lwip/src/apps/mqtt|src/DuetM|src/Networking/LwipEthernet/Lwip/doc" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
+ <entry excluding="src/Hardware/SAME5x/Ethernet|src/Networking/LwipEthernet/Lwip/src/apps/snmp|src/Networking/LwipEthernet/Lwip/src/apps/smtp|src/Hardware/SAME70|src/Networking/LwipEthernet|src/DuetNG|src/Networking/LwipEthernet/Lwip/src/apps/tftp|src/Networking/W5500Ethernet|src/Networking/LwipEthernet/Lwip/src/netif/ppp|src/Networking/LwipEthernet/Lwip/src/apps/lwiperf|src/Networking/LwipEthernet/Lwip/src/apps/altcp_tls|src/Networking/LwipEthernet/Lwip/src/apps/sntp|src/Networking/LwipEthernet/Lwip/src/apps/http|src/Duet3_V06|src/Hardware/SAM4E|src/Pccb|src/Hardware/SAM4S|src/Networking/LwipEthernet/Lwip/src/apps/mqtt|src/DuetM|src/Networking/LwipEthernet/Lwip/doc" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>
@@ -2219,9 +2221,13 @@
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope" versionNumber="2">
+ <configuration configurationName="FMDC_V02_Debug"/>
<configuration configurationName="Duet3ATE"/>
<configuration configurationName="Duet2"/>
<configuration configurationName="Duet3_Debug"/>
+ <configuration configurationName="FMDC_V02">
+ <resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
+ </configuration>
<configuration configurationName="Duet3Mini5plus"/>
<configuration configurationName="DuetMaestro"/>
<configuration configurationName="Duet2_SBC"/>
@@ -2230,18 +2236,14 @@
</configuration>
<configuration configurationName="Duet3MiniATE"/>
<configuration configurationName="Duet3Mini5plus_Debug"/>
+ <configuration configurationName="FMDC_V03">
+ <resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
+ </configuration>
<configuration configurationName="PCCB_10"/>
<configuration configurationName="Duet3_MB6HC">
<resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
</configuration>
<configuration configurationName="Duet3_CAN0"/>
- <configuration configurationName="FMDC_V02">
- <resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
- </configuration>
- <configuration configurationName="FMDC_V02_Debug"/>
- <configuration configurationName="FMDC_V03">
- <resource resourceType="PROJECT" workspacePath="/RepRapFirmware"/>
- </configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
diff --git a/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/.settings/org.eclipse.cdt.managedbuilder.core.prefs
index eb6b0954..b620fe07 100644
--- a/.settings/org.eclipse.cdt.managedbuilder.core.prefs
+++ b/.settings/org.eclipse.cdt.managedbuilder.core.prefs
@@ -7,6 +7,14 @@ environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.releas
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1745168887/C_INCLUDE_PATH/operation=remove
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1745168887/append=true
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1745168887/appendContributed=true
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/CPATH/delimiter=;
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/CPATH/operation=remove
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/CPLUS_INCLUDE_PATH/delimiter=;
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/CPLUS_INCLUDE_PATH/operation=remove
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/C_INCLUDE_PATH/delimiter=;
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/C_INCLUDE_PATH/operation=remove
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/append=true
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/appendContributed=true
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622/CPATH/delimiter=;
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622/CPATH/operation=remove
environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622/CPLUS_INCLUDE_PATH/delimiter=;
@@ -19,6 +27,10 @@ environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.releas
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1745168887/LIBRARY_PATH/operation=remove
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1745168887/append=true
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1745168887/appendContributed=true
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/LIBRARY_PATH/delimiter=;
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/LIBRARY_PATH/operation=remove
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/append=true
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.1275216290.274082366.1645191116.1852610203.289083307.712841925/appendContributed=true
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622/LIBRARY_PATH/delimiter=;
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622/LIBRARY_PATH/operation=remove
environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622/append=true
diff --git a/Developer-documentation/OpenAPI.yaml b/Developer-documentation/OpenAPI.yaml
new file mode 100644
index 00000000..470784d7
--- /dev/null
+++ b/Developer-documentation/OpenAPI.yaml
@@ -0,0 +1,577 @@
+openapi: 3.0.0
+info:
+ version: '3.4.1'
+ title: 'RepRapFirmware'
+ license:
+ name: GPL-3.0
+paths:
+ /rr_connect:
+ get:
+ summary: |
+ Attempt to create a new connection and log in using the (optional) password
+ parameters:
+ - name: password
+ in: query
+ description: 'Password'
+ required: false
+ schema:
+ type: string
+ format: password
+ - name: 'time'
+ in: query
+ description: 'Current datetime that will be used to update RepRapFirmware''s internal clock'
+ required: false
+ schema:
+ type: string
+ format: 'date-time'
+ responses:
+ '200':
+ description: 'Connect response'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'Error code (0 = success, 1 = invalid password, 2 = no more sessions available)'
+ type: number
+ minimum: 0
+ maximum: 2
+ sessionTimeout:
+ type: number
+ minimum: 0
+ boardType:
+ type: string
+ /rr_disconnect:
+ get:
+ summary: |
+ Disconnect again from the RepRapFirmware controller
+ responses:
+ '200':
+ description: 'Disconnect response'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'Error code (0 = success, 1 = failed to remove session)'
+ minimum: 0
+ maximum: 1
+ /rr_status:
+ get:
+ deprecated: true
+ summary: |
+ Retrieve a status response from RepRapFirmware in JSON format. Deprecated in RRF 3.0 and later, use `rr_model` instead
+ parameters:
+ - name: 'type'
+ in: query
+ description: |
+ Type of the status response (defaults to 1)
+
+ - 1: Standard status response
+ - 2: Advanced status response. This also contains fields from the standard status response
+ - 3: Print status response. This contains fields from the standard status response as well as information about the current (print) job
+ required: false
+ schema:
+ type: number
+ minimum: 0
+ maximum: 3
+ responses:
+ '200':
+ description: |
+ Status response content, see [JSON responses](https://github.com/Duet3D/RepRapFirmware/blob/v3-dev/JSON%20responses.md) for further information
+ content:
+ application/json: {}
+ '503':
+ description: 'Insufficient RAM to provide the full response'
+ /rr_config:
+ get:
+ deprecated: true
+ summary: |
+ Retrieve the configuration response. This request provides a JSON object with values that are expected to change rarely. Deprecated in RRF 3.0 and later, use rr_model instead
+ responses:
+ '200':
+ description: |
+ Config response content, see [JSON responses](https://github.com/Duet3D/RepRapFirmware/blob/v3-dev/JSON%20responses.md) for further information
+ content:
+ application/json: {}
+ '503':
+ description: 'Insufficient RAM to provide the full response'
+ /rr_gcode:
+ get:
+ summary: |
+ Execute arbitrary G/M/T-code(s)
+ parameters:
+ - name: 'gcode'
+ in: query
+ description: 'G/M/T-code to execute. This parameter must be present although it can be empty'
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: 'Info about the G-code buffer'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ bufferSpace:
+ description: 'How much buffer space for new G/M/T-codes is still available'
+ type: number
+ minimum: 0
+ /rr_reply:
+ get:
+ summary: |
+ Retrieve the last G-code reply.
+
+ The G-code reply is buffered per connected HTTP client and it is discarded when every HTTP client has fetched it or when the firmware is short on memory and the client has not requested it within reasonable time (1 second)
+ responses:
+ '200':
+ description: 'G-code reply'
+ content:
+ text/plain: {}
+ /rr_upload:
+ get:
+ summary: |
+ Get the last file upload result
+ responses:
+ '200':
+ description: 'Last upload result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'Last file upload result (can be either `0` if the last upload successfully finished or `1` if an error occurred)'
+ type: number
+ minimum: 0
+ post:
+ summary: |
+ Upload a file
+ parameters:
+ - name: 'name'
+ in: query
+ description: 'Path to the file to upload'
+ required: true
+ schema:
+ type: string
+ - name: 'time'
+ in: query
+ description: 'ISO8601-like represenation of the time the file was last modified'
+ required: false
+ schema:
+ type: string
+ format: 'date-time'
+ - name: 'crc32'
+ in: query
+ description: 'CRC32 checksum of the file content as hex string *without* leading `0x`. Usage of this parameter is encouraged'
+ required: false
+ schema:
+ type: string
+ requestBody:
+ description: 'File content'
+ content:
+ application/octet-stream: {}
+ responses:
+ '200':
+ description: 'File upload result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'File upload result (can be either `0` if the last upload uccessfully finished or `1` if an error occurred [e.g. CRC mismatch])'
+ type: number
+ minimum: 0
+ /rr_download:
+ get:
+ summary: |
+ Download a file
+ parameters:
+ - name: 'name'
+ in: query
+ description: 'Path to the file to download'
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: 'Downloaded file'
+ content:
+ application/octet-stream: {}
+ '404':
+ description: 'File not found'
+ /rr_delete:
+ get:
+ summary: |
+ Delete a file or directory
+ parameters:
+ - name: 'name'
+ in: query
+ description: 'Name of the file to delete'
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: 'File delete result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'File delete result (either `0` on success or `1` on error)'
+ type: number
+ minimum: 0
+ /rr_filelist:
+ get:
+ summary: |
+ Retrieve a (partial) file list
+ parameters:
+ - name: 'dir'
+ in: query
+ description: 'Directory to query'
+ required: true
+ schema:
+ type: string
+ - name: 'first'
+ in: query
+ description: 'First item index to return (defaults to `0`)'
+ required: false
+ schema:
+ type: number
+ minimum: 0
+ responses:
+ '200':
+ description: 'File list result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ dir:
+ description: 'Queried directory'
+ type: string
+ first:
+ description: 'First item index returned'
+ type: number
+ minimum: 0
+ files:
+ type: array
+ items:
+ description: 'Files or directories'
+ type: object
+ properties:
+ type:
+ description: 'Type of this item (d = directory, f = file)'
+ enum:
+ - d
+ - f
+ name:
+ description: 'Filename of this item'
+ type: string
+ size:
+ description: 'Size of this item in bytes'
+ type: number
+ minimum: 0
+ date:
+ description: 'Last modified datetime of this item'
+ type: string
+ format: 'date-time'
+ next:
+ description: 'Index of the next item to query or 0 if there are no more items'
+ type: number
+ minimum: 0
+ err:
+ description: |
+ Error code
+
+ - `0`: List query successful
+ - `1`: Drive is not mounted
+ - `2`: Directory does not exist
+ type: number
+ minimum: 0
+ maximum: 2
+ '503':
+ description: 'Insufficient RAM to provide a response'
+ /rr_files:
+ get:
+ summary: |
+ Retrieve a list of files without any attributes
+ parameters:
+ - name: 'dir'
+ in: query
+ description: 'Directory to query'
+ required: true
+ schema:
+ type: string
+ - name: 'first'
+ in: query
+ description: 'First item index to return (defaults to `0`)'
+ required: false
+ schema:
+ type: number
+ minimum: 0
+ - name: 'flagDirs'
+ in: query
+ description: 'Prefix directories with a `*`'
+ schema:
+ type: number
+ minimum: 0
+ maximum: 1
+ responses:
+ '200':
+ description: 'File list result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ dir:
+ description: 'Queried directory'
+ type: string
+ first:
+ description: 'First item index returned'
+ type: number
+ minimum: 0
+ files:
+ type: array
+ items:
+ description: 'Files or directories'
+ type: string
+ next:
+ description: 'Index of the next item to query or 0 if there are no more items'
+ type: number
+ minimum: 0
+ err:
+ description: |
+ Error code
+
+ - `0`: List query successful
+ - `1`: Drive is not mounted
+ - `2`: Directory does not exist
+ type: number
+ minimum: 0
+ maximum: 2
+ '503':
+ description: 'Insufficient RAM to provide a response'
+ /rr_model:
+ get:
+ summary: |
+ Retrieve object model information like [M409](https://duet3d.dozuki.com/Wiki/Gcode#Section_M409_Query_object_model). Supported in RRF 3. and later
+ parameters:
+ - name: 'key'
+ in: query
+ description: 'Key to query'
+ required: true
+ schema:
+ type: string
+ - name: 'flags'
+ in: query
+ description: 'Query flags'
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: 'Object model result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ key:
+ description: 'Queried key'
+ type: string
+ flags:
+ description: 'Query flags'
+ type: string
+ result:
+ type: object
+ '503':
+ description: 'Insufficient RAM to provide a response'
+ /rr_move:
+ get:
+ summary: |
+ Move a file or directory
+ parameters:
+ - name: 'old'
+ in: query
+ description: 'Current path to the file or directory'
+ required: true
+ schema:
+ type: string
+ - name: 'new'
+ in: query
+ description: 'New path of the file or directory'
+ required: true
+ schema:
+ type: string
+ - name: 'deleteexisting'
+ in: query
+ description: 'Set this to `yes` to delete the new file if it already exists. Defaults to `no`'
+ required: false
+ schema:
+ type: string
+ enum: ['yes', 'no']
+ responses:
+ '200':
+ description: 'File move result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'File move result (either `0` on success or `1` on error)'
+ type: number
+ minimum: 0
+ /rr_mkdir:
+ get:
+ summary: |
+ Create a new directory
+ parameters:
+ - name: 'dir'
+ in: query
+ description: 'Directory to create'
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: 'Directory create result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'Directory create result (either `0` on success or `1` on error)'
+ type: number
+ minimum: 0
+ /rr_fileinfo:
+ get:
+ summary: |
+ Parse a G-code job file and return retrieved information. If no file is specified, information about the file being printed is returned
+ parameters:
+ - name: 'name'
+ in: query
+ description: 'Path to the file to parse'
+ required: false
+ schema:
+ type: string
+ responses:
+ '200':
+ description: 'File list result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ err:
+ description: 'Error code, either `0` on success or `1` on error'
+ type: number
+ fileName:
+ description: 'Filename of the G-code file'
+ type: string
+ size:
+ description: 'Size of the file'
+ type: number
+ lastModified:
+ description: 'Datetime when the file was last modified'
+ type: string
+ format: 'date-time'
+ height:
+ description: 'Object height (in mm)'
+ type: number
+ layerHeight:
+ description: 'Layer height (in mm)'
+ type: number
+ numLayers:
+ description: 'Number of layers'
+ type: number
+ printTime:
+ description: 'Expected time to print (in s)'
+ type: number
+ simulatedTime:
+ description: 'Simulated time to print (in s)'
+ type: number
+ filament:
+ description: 'Filament usage (in mm)'
+ type: array
+ items:
+ type: number
+ printDuration:
+ description: 'Current print duration (in s)'
+ type: number
+ thumbnails:
+ description: 'Info about embedded thumbnails'
+ items:
+ type: object
+ properties:
+ width:
+ description: 'Width of the thumbnail (in px)'
+ type: number
+ height:
+ description: 'Height of the thumbnail (in px)'
+ type: number
+ format:
+ description: 'Thumbnail format'
+ type: string
+ enum: ['png', 'qoi', 'jpeg']
+ offset:
+ description: 'File offset (to be used with rr_thumbnail)'
+ type: number
+ size:
+ description: 'Size of the thumbnail'
+ type: number
+ generatedBy:
+ description: 'Application that generated the file'
+ type: string
+ /rr_thumbnail:
+ get:
+ summary: |
+ Query a thumbnail from a G-code file
+ parameters:
+ - name: 'name'
+ in: query
+ description: 'Filename to query'
+ required: true
+ schema:
+ type: string
+ - name: 'offset'
+ in: query
+ description: 'File offset of the thumbnail'
+ required: true
+ schema:
+ type: number
+ responses:
+ '200':
+ description: 'File list result'
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ fileName:
+ description: 'Filename of the G-code file'
+ type: string
+ offset:
+ description: 'Offset of the thumbnail'
+ type: number
+ data:
+ description: 'Base64-encoded thumbnail data'
+ type: string
+ next:
+ description: 'Next thumbnail offset or 0 if complete'
+ type: number
+ minimum: 0
+ err:
+ description: 'Error code, either `0` on success or `1` on error'
+ type: number
+
diff --git a/src/Accelerometers/Accelerometers.cpp b/src/Accelerometers/Accelerometers.cpp
index c32ae54f..7b1835a8 100644
--- a/src/Accelerometers/Accelerometers.cpp
+++ b/src/Accelerometers/Accelerometers.cpp
@@ -15,7 +15,7 @@
#include <GCodes/GCodeBuffer/GCodeBuffer.h>
#include <RTOSIface/RTOSIface.h>
#include <Platform/TaskPriorities.h>
-#include <Hardware/SharedSpi/SharedSpiDevice.h>
+#include <Hardware/Spi/SharedSpiDevice.h>
#if SUPPORT_CAN_EXPANSION
# include <CanMessageFormats.h>
diff --git a/src/Accelerometers/LIS3DH.h b/src/Accelerometers/LIS3DH.h
index 22fe4751..23632e58 100644
--- a/src/Accelerometers/LIS3DH.h
+++ b/src/Accelerometers/LIS3DH.h
@@ -12,7 +12,7 @@
#if SUPPORT_ACCELEROMETERS
-#include <Hardware/SharedSpi/SharedSpiClient.h>
+#include <Hardware/Spi/SharedSpiClient.h>
class LIS3DH : public SharedSpiClient
{
diff --git a/src/CAN/CanInterface.cpp b/src/CAN/CanInterface.cpp
index 2ddde129..9cb8b0c0 100644
--- a/src/CAN/CanInterface.cpp
+++ b/src/CAN/CanInterface.cpp
@@ -1071,6 +1071,15 @@ pre(driver.IsRemote())
cons.PopulateFromCommand(gb);
return cons.SendAndGetResponse(CanMessageType::m569p7, driver.boardAddress, reply);
}
+#if DUAL_CAN
+ case 8: // read axis force via secondary CAN
+ {
+ if (reprap.GetMove().GetKinematics().GetKinematicsType() == KinematicsType::hangprinter) {
+ return HangprinterKinematics::ReadODrive3AxisForce(driver, reply);
+ }
+ return GCodeResult::errorNotSupported;
+ }
+#endif
default:
return GCodeResult::errorNotSupported;
@@ -1580,7 +1589,7 @@ bool CanInterface::ODrive::GetExpectedSimpleMessage(CanMessageBuffer *buf, Drive
int count = 0;
bool ok = true;
do{
- ok = ReceivePlainMessage(buf, MaxResponseSendWait);
+ ok = ReceivePlainMessage(buf);
count++;
} while (ok && buf->id != expectedId && count < 5);
diff --git a/src/Comms/PanelDueUpdater.cpp b/src/Comms/PanelDueUpdater.cpp
index bd381e6d..164bf130 100644
--- a/src/Comms/PanelDueUpdater.cpp
+++ b/src/Comms/PanelDueUpdater.cpp
@@ -24,8 +24,6 @@ public:
SerialPort::StopBit stop = SerialPort::StopBitOne) noexcept override { return true; }
void close() noexcept override {}
- bool isUsb() noexcept override { return false; }
-
int read(uint8_t* data, int size) noexcept override;
int write(const uint8_t* data, int size) noexcept override { return this->uart.write(data, size); }
int get() noexcept override;
@@ -33,8 +31,6 @@ public:
bool timeout(int millisecs) noexcept override { _timeout = millisecs; return true; }
void flush() noexcept override { this->uart.flush(); }
- void setDTR(bool dtr) noexcept override {}
- void setRTS(bool rts) noexcept override {}
private:
AsyncSerial& uart;
diff --git a/src/Config/Configuration.h b/src/Config/Configuration.h
index 1fd6f7b1..51eae063 100644
--- a/src/Config/Configuration.h
+++ b/src/Config/Configuration.h
@@ -22,6 +22,13 @@ Licence: GPL
#include <cstddef> // for size_t
#include <cstring> // for strlen
+// Motion systems
+#if SUPPORT_ASYNC_MOVES
+constexpr unsigned int NumMovementSystems = 2; // for now we support only two motion systems
+#else
+constexpr unsigned int NumMovementSystems = 1;
+#endif
+
// Axes
constexpr float DefaultAxisMaxFeedrate = 100.0; // mm/sec
constexpr float DefaultZMaxFeedrate = 5.0;
@@ -52,6 +59,8 @@ constexpr float DefaultTravelAcceleration = 20000.0; // higher than the likely m
constexpr float DefaultAxisMinimum = 0.0;
constexpr float DefaultAxisMaximum = 200.0;
+constexpr float DefaultFilamentDiameter = 1.75; // the default filament diameter assumed
+
constexpr unsigned int MaxTools = 50; // this limit is to stop the serialised object model getting too large
constexpr unsigned int MinVisibleAxes = 2; // the minimum number of axes that we allow to be visible
@@ -200,7 +209,6 @@ constexpr size_t RESERVED_OUTPUT_BUFFERS = 2; // Number of reserved ou
constexpr size_t maxQueuedCodes = 16; // How many codes can be queued?
-// These two definitions are only used if TRACK_OBJECT_NAMES is defined, however that definition isn't available in this file
#if SAME70 || SAME5x
constexpr size_t MaxTrackedObjects = 40; // How many build plate objects we track. Each one needs 16 bytes of storage, in addition to the string space.
constexpr size_t ObjectNamesStringSpace = 1000; // How much space we reserve for the names of objects on the build plate
@@ -235,7 +243,7 @@ constexpr float DefaultIdleCurrentFactor = 0.3; // Proportion of normal motor
constexpr uint32_t DefaultGracePeriod = 10; // how long we wait for more moves to become available before starting movement
constexpr float DefaultNonlinearExtrusionLimit = 0.2; // Maximum additional commanded extrusion to compensate for nonlinearity
-constexpr size_t NumRestorePoints = 6; // Number of restore points, must be at least 3
+constexpr size_t NumVisibleRestorePoints = 6; // Number of restore points, must be at least 3
constexpr float AxisRoundingError = 0.02; // Maximum possible error when we round trip a machine position to motor coordinates and back
@@ -246,6 +254,9 @@ constexpr float FILAMENT_WIDTH = 1.75; // Millimetres
constexpr unsigned int MaxStackDepth = 10; // Maximum depth of stack (was 5 in 3.01-RC2, increased to 7 for 3.01-RC3, 10 for 3.4.0beta6)
// CNC and laser support
+constexpr float DefaultMinSpindlePwm = 0.0; // Default minimum PWM level for spindle control
+constexpr float DefaultMaxSpindlePwm = 1.0; // Default maximum PWM level for spindle control
+constexpr float DefaultIdleSpindlePwm = 0.0; // Default idle PWM level for spindle control
constexpr int32_t DefaultMinSpindleRpm = 60; // Default minimum available spindle RPM
constexpr int32_t DefaultMaxSpindleRpm = 10000; // Default spindle RPM at full PWM
constexpr float DefaultMaxLaserPower = 255.0; // Power setting in M3 command for full power
diff --git a/src/Config/Pins.h b/src/Config/Pins.h
index 7e34e13d..90574b63 100644
--- a/src/Config/Pins.h
+++ b/src/Config/Pins.h
@@ -68,6 +68,18 @@
# define SUPPORT_12864_LCD 0
#endif
+#ifndef SUPPORT_ILI9488_LCD
+# define SUPPORT_ILI9488_LCD 0
+#endif
+
+#ifndef USE_FONT_CHIP
+# define USE_FONT_CHIP 0
+#endif
+
+#define SUPPORT_DIRECT_LCD (SUPPORT_12864_LCD || SUPPORT_ILI9488_LCD)
+#define SUPPORT_ROTARY_ENCODER SUPPORT_12864_LCD
+#define SUPPORT_RESISTIVE_TOUCH SUPPORT_ILI9488_LCD
+
#ifndef SUPPORT_LED_STRIPS
# define SUPPORT_LED_STRIPS 0
#endif
@@ -76,6 +88,10 @@
# define SUPPORT_SPI_SENSORS 1
#endif
+#ifndef SUPPORT_BME280
+# define SUPPORT_BME280 0
+#endif
+
#define HAS_AUX_DEVICES (defined(SERIAL_AUX_DEVICE)) // if SERIAL_AUX_DEVICE is defined then we have one or more aux devices
#ifndef SUPPORT_PANELDUE_FLASH
@@ -122,10 +138,6 @@
# define SUPPORT_OBJECT_MODEL 0
#endif
-#ifndef TRACK_OBJECT_NAMES
-# define TRACK_OBJECT_NAMES 0
-#endif
-
#define HAS_SMART_DRIVERS (SUPPORT_TMC2660 || SUPPORT_TMC22xx || SUPPORT_TMC51xx)
#ifndef HAS_STALL_DETECT
# define HAS_STALL_DETECT (SUPPORT_TMC2660 || SUPPORT_TMC51xx)
@@ -226,6 +238,10 @@
# define SUPPORT_ACCELEROMETERS 0
#endif
+#ifndef SUPPORT_PROBE_POINTS_FILE
+# define SUPPORT_PROBE_POINTS_FILE 0
+#endif
+
// Optional kinematics support, to allow us to reduce flash memory usage
#ifndef SUPPORT_LINEAR_DELTA
# define SUPPORT_LINEAR_DELTA 1
diff --git a/src/Config/Pins_Duet3Mini.h b/src/Config/Pins_Duet3Mini.h
index 3ca4186b..859e23e5 100644
--- a/src/Config/Pins_Duet3Mini.h
+++ b/src/Config/Pins_Duet3Mini.h
@@ -40,6 +40,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20038000;
#define HAS_CPU_TEMP_SENSOR 1 // enable this as an experiment - it may be better than nothing
#define SUPPORT_TMC22xx 1
+#define SUPPORT_TMC2240 1
#define HAS_STALL_DETECT 1
#define HAS_VOLTAGE_MONITOR 1
@@ -50,11 +51,11 @@ constexpr uint32_t IAP_IMAGE_START = 0x20038000;
#define SUPPORT_LED_STRIPS 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
#define SUPPORT_SCANNER 1 // set zero to disable support for FreeLSS scanners
#define SUPPORT_LASER 1 // support laser cutters and engravers using G1 S parameter
#define SUPPORT_IOBITS 1 // set to support P parameter in G0/G1 commands
#define SUPPORT_DHT_SENSOR 1 // set nonzero to support DHT temperature/humidity sensors (requires RTOS)
+#define SUPPORT_BME280 1
#define SUPPORT_WORKPLACE_COORDINATES 1 // set nonzero to support G10 L2 and G53..59
#define SUPPORT_12864_LCD 1 // set nonzero to support 12864 LCD and rotary encoder
#define SUPPORT_ACCELEROMETERS 1
@@ -62,8 +63,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20038000;
#define SUPPORT_FTP 1
#define SUPPORT_TELNET 1
#define SUPPORT_ASYNC_MOVES 1
-#define ALLOCATE_DEFAULT_PORTS 0
-#define TRACK_OBJECT_NAMES 1
+#define SUPPORT_PROBE_POINTS_FILE 1
#define USE_CACHE 1 // set nonzero to enable the cache
#define USE_MPU 0 // set nonzero to enable the memory protection unit
@@ -158,7 +158,6 @@ constexpr Pin TMC22xxMuxPins[1] = { PortDPin(0) };
#define TMC22xx_VARIABLE_NUM_DRIVERS 0
#define TMC22xx_SINGLE_DRIVER 0
#define TMC22xx_USE_SLAVEADDR 1
-#define TMC22xx_DEFAULT_STEALTHCHOP 0
// Define the baud rate used to send/receive data to/from the drivers.
// If we assume a worst case clock frequency of 8MHz then the maximum baud rate is 8MHz/16 = 500kbaud.
@@ -174,7 +173,14 @@ constexpr float DriverVRef = 180.0; // in mV
constexpr float DriverFullScaleCurrent = DriverVRef/DriverSenseResistor; // in mA
constexpr float DriverCsMultiplier = 32.0/DriverFullScaleCurrent;
constexpr float MaximumMotorCurrent = 2000.0;
-constexpr float MaximumStandstillCurrent = 1500.0;
+//constexpr float MaximumStandstillCurrent = 1500.0; // this is not currently enforced
+
+#if SUPPORT_TMC2240
+constexpr float Tmc2240Rref = 14.0; // TMC2240 reference resistor on Duet 3 Mini2+ prototype, in Kohms
+constexpr float Tmc2240FullScaleCurrent = 36000/Tmc2240Rref; // in mA, assuming we set the range bits in the DRV_CONF register to 01b
+constexpr float Tmc2240CsMultiplier = 32.0/Tmc2240FullScaleCurrent;
+constexpr float MaximumTmc2240MotorCurrent = 2500.0;
+#endif
// Thermistors
constexpr Pin TEMP_SENSE_PINS[NumThermistorInputs] = { PortCPin(0), PortCPin(1), PortCPin(2) }; // Thermistor pin numbers
diff --git a/src/Config/Pins_Duet3_MB6HC.h b/src/Config/Pins_Duet3_MB6HC.h
index 1517acdd..988527bb 100644
--- a/src/Config/Pins_Duet3_MB6HC.h
+++ b/src/Config/Pins_Duet3_MB6HC.h
@@ -42,11 +42,11 @@ constexpr uint32_t IAP_IMAGE_START = 0x20458000; // last 32kb of RAM
#define DUAL_CAN 1 // support the second CAN interface as simple CAN (not FD)
#define SUPPORT_LED_STRIPS 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
#define SUPPORT_SCANNER 0 // set zero to disable support for FreeLSS scanners
#define SUPPORT_LASER 1 // support laser cutters and engravers using G1 S parameter
#define SUPPORT_IOBITS 1 // set to support P parameter in G0/G1 commands
#define SUPPORT_DHT_SENSOR 1 // set nonzero to support DHT temperature/humidity sensors
+#define SUPPORT_BME280 1
#define SUPPORT_ACCELEROMETERS 1
#define SUPPORT_WORKPLACE_COORDINATES 1 // set nonzero to support G10 L2 and G53..59
#define SUPPORT_OBJECT_MODEL 1
@@ -54,8 +54,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20458000; // last 32kb of RAM
#define SUPPORT_TELNET 1
#define SUPPORT_MULTICAST_DISCOVERY 1
#define SUPPORT_ASYNC_MOVES 1
-#define ALLOCATE_DEFAULT_PORTS 0
-#define TRACK_OBJECT_NAMES 1
+#define SUPPORT_PROBE_POINTS_FILE 1
#define USE_MPU 1 // Needed if USE_CACHE is set, so that we can have non-cacheable memory regions
#define USE_CACHE 1
diff --git a/src/Config/Pins_Duet3_MB6XD.h b/src/Config/Pins_Duet3_MB6XD.h
index c8a296e4..b680527d 100644
--- a/src/Config/Pins_Duet3_MB6XD.h
+++ b/src/Config/Pins_Duet3_MB6XD.h
@@ -38,11 +38,11 @@ constexpr uint32_t IAP_IMAGE_START = 0x20458000; // last 32kb of RAM
#define DUAL_CAN 1 // support the second CAN interface as simple CAN (not FD)
#define SUPPORT_LED_STRIPS 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
#define SUPPORT_SCANNER 0 // set zero to disable support for FreeLSS scanners
#define SUPPORT_LASER 1 // support laser cutters and engravers using G1 S parameter
#define SUPPORT_IOBITS 1 // set to support P parameter in G0/G1 commands
#define SUPPORT_DHT_SENSOR 1 // set nonzero to support DHT temperature/humidity sensors
+#define SUPPORT_BME280 1
#define SUPPORT_ACCELEROMETERS 1
#define SUPPORT_WORKPLACE_COORDINATES 1 // set nonzero to support G10 L2 and G53..59
#define SUPPORT_OBJECT_MODEL 1
@@ -50,8 +50,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20458000; // last 32kb of RAM
#define SUPPORT_TELNET 1
#define SUPPORT_MULTICAST_DISCOVERY 1
#define SUPPORT_ASYNC_MOVES 1
-#define ALLOCATE_DEFAULT_PORTS 0
-#define TRACK_OBJECT_NAMES 1
+#define SUPPORT_PROBE_POINTS_FILE 1
#define USE_MPU 1 // Needed if USE_CACHE is set, so that we can have non-cacheable memory regions
#define USE_CACHE 1
diff --git a/src/Config/Pins_DuetM.h b/src/Config/Pins_DuetM.h
index 9138226c..b0ecf33b 100644
--- a/src/Config/Pins_DuetM.h
+++ b/src/Config/Pins_DuetM.h
@@ -32,7 +32,6 @@ constexpr uint32_t IAP_IMAGE_START = 0x20018000;
#define HAS_VREF_MONITOR 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
#define SUPPORT_SCANNER 0 // set zero to disable support for FreeLSS scanners
#define SUPPORT_LASER 1 // support laser cutters and engravers using G1 S parameter
#define SUPPORT_IOBITS 0 // set to support P parameter in G0/G1 commands
@@ -43,9 +42,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20018000;
#define SUPPORT_OBJECT_MODEL 1
#define SUPPORT_FTP 1
#define SUPPORT_TELNET 1
-#define SUPPORT_ASYNC_MOVES 1
-#define ALLOCATE_DEFAULT_PORTS 0
-#define TRACK_OBJECT_NAMES 1
+#define SUPPORT_ASYNC_MOVES 0
// The physical capabilities of the machine
diff --git a/src/Config/Pins_DuetNG.h b/src/Config/Pins_DuetNG.h
index e9c507dd..e79d47de 100644
--- a/src/Config/Pins_DuetNG.h
+++ b/src/Config/Pins_DuetNG.h
@@ -60,12 +60,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20018000; // IAP is loaded into the last
#define ACTIVE_LOW_HEAT_ON 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
-#if defined(USE_SBC)
-# define SUPPORT_SCANNER 0
-#else
-# define SUPPORT_SCANNER 1 // set zero to disable support for FreeLSS scanners
-#endif
+#define SUPPORT_SCANNER 0 // set zero to disable support for FreeLSS scanners
#define SUPPORT_LASER 1 // support laser cutters and engravers using G1 S parameter
#define SUPPORT_IOBITS 1 // set to support P parameter in G0/G1 commands
#define SUPPORT_DHT_SENSOR 1 // set nonzero to support DHT temperature/humidity sensors
@@ -91,9 +86,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20018000; // IAP is loaded into the last
# define SUPPORT_TELNET 1
#endif
-#define SUPPORT_ASYNC_MOVES 1
-#define ALLOCATE_DEFAULT_PORTS 0
-#define TRACK_OBJECT_NAMES 1
+#define SUPPORT_ASYNC_MOVES 0
#define USE_CACHE 1 // set nonzero to enable the cache
#define USE_MPU 0 // set nonzero to enable the memory protection unit
diff --git a/src/Config/Pins_FMDC.h b/src/Config/Pins_FMDC.h
index 982569a3..bc2c9760 100644
--- a/src/Config/Pins_FMDC.h
+++ b/src/Config/Pins_FMDC.h
@@ -34,6 +34,7 @@ constexpr uint32_t IAP_IMAGE_START = 0x20028000;
#define HAS_CPU_TEMP_SENSOR 1 // enable this as an experiment - it may be better than nothing
#define SUPPORT_TMC22xx 1
+#define SUPPORT_TMC2240 0
#define HAS_STALL_DETECT 1
#define HAS_VOLTAGE_MONITOR 1
@@ -44,21 +45,19 @@ constexpr uint32_t IAP_IMAGE_START = 0x20028000;
#define SUPPORT_LED_STRIPS 0
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
#define SUPPORT_SCANNER 0 // set zero to disable support for FreeLSS scanners
#define SUPPORT_LASER 1 // support laser cutters and engravers using G1 S parameter
#define SUPPORT_IOBITS 0 // set to support P parameter in G0/G1 commands
#define SUPPORT_DHT_SENSOR 1 // set nonzero to support DHT temperature/humidity sensors (requires RTOS)
#define SUPPORT_WORKPLACE_COORDINATES 1 // set nonzero to support G10 L2 and G53..59
#define SUPPORT_12864_LCD 0 // set nonzero to support 12864 LCD and rotary encoder
-#define SUPPORT_TFTM0356_6_LCD 1
+#define SUPPORT_ILI9488_LCD 1
+#define USE_FONT_CHIP 1
#define SUPPORT_ACCELEROMETERS 1
#define SUPPORT_OBJECT_MODEL 1
#define SUPPORT_FTP 0
#define SUPPORT_TELNET 0
#define SUPPORT_ASYNC_MOVES 0
-#define ALLOCATE_DEFAULT_PORTS 0
-#define TRACK_OBJECT_NAMES 1
#define SUPPORT_PANELDUE_FLASH 0
#define SUPPORT_SPI_SENSORS 0
@@ -214,6 +213,7 @@ constexpr Pin BeeperPins[2] = { PortBPin(18), PortBPin(19) };
constexpr Pin SdCardDetectPins[NumSdCards] = { PortBPin(16), /*PortBPin(0)*/ NoPin };
constexpr Pin SdMciPins[] = { PortAPin(20), PortAPin(21), PortBPin(18), PortBPin(19), PortBPin(20), PortBPin(21) };
constexpr GpioPinFunction SdMciPinsFunction = GpioPinFunction::I;
+Sdhc * const SdhcDevice = SDHC1;
constexpr IRQn_Type SdhcIRQn = SDHC1_IRQn;
constexpr Pin BeeperPins[2] = { PortAPin(8), PortAPin(9) };
@@ -223,24 +223,29 @@ constexpr Pin SdSpiCSPins[NumSdCards - HAS_HIGH_SPEED_SD] = { PortCPin(14) };
constexpr uint32_t ExpectedSdCardSpeed = 15000000;
// LCD interface
-constexpr uint32_t LcdSpiClockFrequency = 4000000; // 4.0MHz
+// The maximum permitted SPI speed for the ILI9488 controller is 15.0MHz for write cycles (66ns cycle time) and 6.67MHz for read accesses (150ns cycle time).
+// We use only write accesses.
+// Using an SPI CLK of 60MHz we can only divide by 2, 4, 6 etc.
+// Therefore the available frequencies are 15MHz, 10MHz, 7.5MHz, 6MHz, 5MHz.
+constexpr uint32_t LcdSpiClockFrequency = 15000000;
constexpr unsigned int LcdSercomNumber = 0;
constexpr Pin LcdSpiMosiPin = PortAPin(4);
constexpr Pin LcdSpiMisoPin = PortAPin(7);
constexpr Pin LcdSpiSclkPin = PortAPin(5);
-constexpr GpioPinFunction LcdSpiPinFunction = GpioPinFunction::C;
-
constexpr Pin LcdSpiCsPin = PortAPin(6);
-constexpr Pin LcdRtpPenPin = PortAPin(3);
+constexpr GpioPinFunction LcdSpiPinFunction = GpioPinFunction::D;
+
constexpr Pin LcdDcPin = PortCPin(5);
constexpr Pin LcdResetPin = PortCPin(6);
#if defined(FMDC_V03)
constexpr Pin LcdFlashCsPin = PortAPin(20);
constexpr Pin LcdBacklightPin = PortBPin(16);
+constexpr Pin LcdFontCsPin = PortBPin(20);
#elif defined(FMDC_V02)
constexpr Pin LcdFlashCsPin = PortBPin(10);
constexpr Pin LcdBacklightPin = PortBPin(12);
+constexpr Pin LcdFontCsPin = PortBPin(15);
#endif
constexpr Pin LcdFlashWpPin = PortAPin(2);
@@ -399,7 +404,7 @@ constexpr PinDescription PinTable[] =
#elif defined(FMDC_V02)
{ TcOutput::none, TccOutput::none, AdcInput::none, SercomIo::none, SercomIo::none, Nx, PinCapability::none, nullptr }, // PB18 SDHC DAT0
{ TcOutput::none, TccOutput::none, AdcInput::none, SercomIo::none, SercomIo::none, Nx, PinCapability::none, nullptr }, // PB19 SDHC DAT1
- { TcOutput::none, TccOutput::none, AdcInput::none, SercomIo::none, SercomIo::none, Nx, PinCapability::none, nullptr }, // PB20 SDHC DAT2
+ { TcOutput::none, TccOutput::none, AdcInput::none, SercomIo::none, SercomIo::none, Nx, PinCapability::none, "ate.lcd.fontcs" }, // PB20 SDHC DAT2
{ TcOutput::none, TccOutput::none, AdcInput::none, SercomIo::none, SercomIo::none, Nx, PinCapability::none, nullptr }, // PB21 SDHC DAT3
#endif
{ TcOutput::none, TccOutput::none, AdcInput::none, SercomIo::none, SercomIo::none, Nx, PinCapability::none, nullptr }, // PB22 crystal XIN1
@@ -473,6 +478,7 @@ constexpr DmaPriority DmacPrioTmcTx = 0;
constexpr DmaPriority DmacPrioTmcRx = 1; // the baud rate is 100kbps so this is not very critical
constexpr DmaPriority DmacPrioWiFi = 2; // high speed SPI in slave mode
constexpr DmaPriority DmacPrioSbc = 2; // high speed SPI in slave mode
+constexpr DmaPriority DmacPrioLcdTx = 3; // high speed SPI in slave mode
// Timer allocation
// TC2 and TC3 are used for step pulse generation and software timers
diff --git a/src/Config/Pins_Pccb.h b/src/Config/Pins_Pccb.h
index a069e505..5f9e50ae 100644
--- a/src/Config/Pins_Pccb.h
+++ b/src/Config/Pins_Pccb.h
@@ -55,7 +55,6 @@ constexpr uint32_t IAP_IMAGE_START = 0x20010000;
#define HAS_VREF_MONITOR 1
#define SUPPORT_INKJET 0 // set nonzero to support inkjet control
-#define SUPPORT_ROLAND 0 // set nonzero to support Roland mill
#define SUPPORT_SCANNER 0 // set zero to disable support for FreeLSS scanners
#define SUPPORT_IOBITS 0 // set to support P parameter in G0/G1 commands
#define SUPPORT_DHT_SENSOR 0 // set nonzero to support DHT temperature/humidity sensors (requires RTOS)
diff --git a/src/Display/ButtonMenuItem.cpp b/src/Display/ButtonMenuItem.cpp
new file mode 100644
index 00000000..0fa31985
--- /dev/null
+++ b/src/Display/ButtonMenuItem.cpp
@@ -0,0 +1,97 @@
+/*
+ * ButtonMenuItem.cpp
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#include "ButtonMenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+ButtonMenuItem::ButtonMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, const char* t, const char* cmd, char const* acFile) noexcept
+ : MenuItem(r, c, w, CentreAlign, fn), text(t), command(cmd), m_acFile(acFile)
+{
+}
+
+void ButtonMenuItem::CorePrint(Lcd& lcd) noexcept
+{
+ lcd.WriteSpaces(1); // space at start in case highlighted
+ lcd.printf("%s", text);
+ lcd.WriteSpaces(1); // space at end to allow for highlighting
+}
+
+void ButtonMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
+{
+ if (IsVisible() && (itemChanged || !drawn || highlight != highlighted) && column < lcd.GetNumCols())
+ {
+ highlighted = highlight;
+ PrintAligned(lcd, rightMargin);
+ itemChanged = false;
+ drawn = true;
+ }
+}
+
+void ButtonMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
+{
+ if (width == 0)
+ {
+ lcd.SetFont(fontNumber);
+ lcd.SetCursor(lcd.GetNumRows(), 0);
+ lcd.SetRightMargin(lcd.GetNumCols());
+ lcd.TextInvert(false);
+ CorePrint(lcd);
+ width = lcd.GetColumn();
+ }
+ if (height == 0)
+ {
+ lcd.SetFont(fontNumber);
+ height = lcd.GetFontHeight();
+ }
+}
+
+// TODO WS1: if we overflow the command or directory string, we should probably offer a return value that tells the caller to do nothing...
+bool ButtonMenuItem::Select(const StringRef& cmd) noexcept
+{
+ const int nReplacementIndex = StringContains(command, "#0");
+ if (-1 != nReplacementIndex)
+ {
+ cmd.copy(command, nReplacementIndex);
+ cmd.cat(m_acFile);
+ }
+ else
+ {
+ cmd.copy(command);
+ if (StringEqualsIgnoreCase(command, "menu") && strlen(m_acFile) != 0)
+ {
+ // For backwards compatibility, if 'menu' is used without any parameters, use the L parameter as the name of the menu file
+ cmd.cat(' ');
+ cmd.cat(m_acFile);
+ }
+ }
+
+ return true;
+}
+
+PixelNumber ButtonMenuItem::GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept
+{
+ PixelNumber tOffsetRequest = tCurrentOffset;
+
+ // Are we off the bottom of the visible window?
+ if (64 + tCurrentOffset <= row + fontHeight + 1)
+ {
+ tOffsetRequest = row - 3;
+ }
+
+ // Should we move back up?
+ if (row < tCurrentOffset + 3)
+ {
+ tOffsetRequest = (row > 3) ? row - 3 : 0;
+ }
+
+ return tOffsetRequest;
+}
+
+#endif
+
+// End
diff --git a/src/Display/ButtonMenuItem.h b/src/Display/ButtonMenuItem.h
new file mode 100644
index 00000000..eed53756
--- /dev/null
+++ b/src/Display/ButtonMenuItem.h
@@ -0,0 +1,38 @@
+/*
+ * ButtonMenuItem.h
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_BUTTONMENUITEM_H_
+#define SRC_DISPLAY_BUTTONMENUITEM_H_
+
+#include "MenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+class ButtonMenuItem final : public MenuItem
+{
+public:
+ DECLARE_FREELIST_NEW_DELETE(ButtonMenuItem)
+
+ ButtonMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, const char *t, const char *cmd, const char *acFile) noexcept;
+ void Draw(Lcd& lcd, PixelNumber maxWidth, bool highlight) noexcept override;
+ void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
+ bool Select(const StringRef& cmd) noexcept override;
+
+ PixelNumber GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept override;
+
+protected:
+ void CorePrint(Lcd& lcd) noexcept override;
+
+private:
+ const char *text;
+ const char *command;
+ const char *m_acFile; // used when action ("command") is "menu"
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_BUTTONMENUITEM_H_ */
diff --git a/src/Display/Display.cpp b/src/Display/Display.cpp
index a69d0517..881307ed 100644
--- a/src/Display/Display.cpp
+++ b/src/Display/Display.cpp
@@ -7,22 +7,45 @@
#include "Display.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
+
+# if SUPPORT_12864_LCD
+# include "Lcd/ST7920/Lcd7920.h"
+# include "Lcd/ST7567/Lcd7567.h"
+# endif
+
+#if SUPPORT_ILI9488_LCD
+# include "Lcd/ILI9488/ILI9488.h"
+# include <AnalogOut.h>
+#endif
-#include "Lcd/ST7920/Lcd7920.h"
-#include "Lcd/ST7567/Lcd7567.h"
#include <GCodes/GCodes.h>
#include <GCodes/GCodeBuffer/GCodeBuffer.h>
#include <Hardware/IoPorts.h>
constexpr int DefaultPulsesPerClick = -4; // values that work with displays I have are 2 and -4
-extern const LcdFont font11x14;
+#if SUPPORT_12864_LCD
extern const LcdFont font7x11;
-
+extern const LcdFont font11x14;
const LcdFont * const fonts[] = { &font7x11, &font11x14 };
constexpr size_t SmallFontNumber = 0;
constexpr size_t LargeFontNumber = 1;
+#endif
+
+#if SUPPORT_ILI9488_LCD
+# if USE_FONT_CHIP
+constexpr size_t SmallFontNumber = 0;
+constexpr size_t LargeFontNumber = 1;
+# else
+// The FMDC prototypes don't have enough flash memory for more than one font
+//extern const LcdFont font19x21;
+extern const LcdFont font28x32;
+const LcdFont * const tftFonts[] = { /*&font19x21 ,*/ &font28x32 };
+constexpr size_t SmallFontNumber = 0;
+constexpr size_t LargeFontNumber = 0;
+# endif
+#endif
constexpr uint32_t NormalRefreshMillis = 250;
constexpr uint32_t FastRefreshMillis = 50;
@@ -41,20 +64,30 @@ constexpr ObjectModelTableEntry Display::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. Display members
+#if SUPPORT_ROTARY_ENCODER
{ "pulsesPerClick", OBJECT_MODEL_FUNC((int32_t)self->encoder->GetPulsesPerClick()), ObjectModelEntryFlags::none },
+#endif
{ "spiFreq", OBJECT_MODEL_FUNC((int32_t)self->lcd->GetSpiFrequency()), ObjectModelEntryFlags::none },
{ "typeName", OBJECT_MODEL_FUNC(self->lcd->GetDisplayTypeName()), ObjectModelEntryFlags::none },
};
-constexpr uint8_t Display::objectModelTableDescriptor[] = { 1, 3 };
+constexpr uint8_t Display::objectModelTableDescriptor[] = { 1, 2 + SUPPORT_ROTARY_ENCODER };
DEFINE_GET_OBJECT_MODEL_TABLE(Display)
#endif
Display::Display() noexcept
- : lcd(nullptr), menu(nullptr), encoder(nullptr), lastRefreshMillis(0),
- mboxSeq(0), mboxActive(false), beepActive(false), updatingFirmware(false)
+ : lcd(nullptr), menu(nullptr),
+#if SUPPORT_ROTARY_ENCODER
+ encoder(nullptr),
+#endif
+#if SUPPORT_RESISTIVE_TOUCH
+ touchController(nullptr),
+#endif
+ beepTicksToGo(0),
+ lastRefreshMillis(0),
+ mboxSeq(0), mboxActive(false), updatingFirmware(false)
{
}
@@ -63,45 +96,62 @@ void Display::Spin() noexcept
{
if (lcd != nullptr && !updatingFirmware)
{
- encoder->Poll();
- // Check encoder and update display
- const int ch = encoder->GetChange();
bool forceRefresh = false;
- if (ch != 0)
+#if SUPPORT_ROTARY_ENCODER
+ if (encoder != nullptr)
{
- menu->EncoderAction(ch);
- forceRefresh = true;
+ encoder->Poll();
+ // Check encoder and update display
+ const int ch = encoder->GetChange();
+ if (ch != 0)
+ {
+ menu->EncoderAction(ch);
+ forceRefresh = true;
+ }
+ else if (encoder->GetButtonPress())
+ {
+ menu->EncoderAction(0);
+ forceRefresh = true;
+ }
}
- else if (encoder->GetButtonPress())
+#endif
+#if SUPPORT_RESISTIVE_TOUCH
+ if (touchController != nullptr)
{
- menu->EncoderAction(0);
- forceRefresh = true;
+ uint16_t x, y;
+ bool repeat;
+ if (touchController->Read(x, y, repeat) && menu != nullptr)
+ {
+ menu->HandleTouch(x, y);
+ }
}
-
- const MessageBox& mbox = reprap.GetMessageBox();
- if (mbox.active)
+#endif
{
- if (!mboxActive || mboxSeq != mbox.seq)
+ ReadLockedPointer<const MessageBox> mbox = reprap.GetCurrentMessageBox();
+ if (mbox.IsNotNull() && mbox->IsLegacyType())
{
- // New message box to display
- if (!mboxActive)
+ if (!mboxActive || mboxSeq != mbox->GetSeq())
{
- menu->ClearHighlighting(); // cancel highlighting and adjustment
- menu->Refresh();
+ // New message box to display
+ if (!mboxActive)
+ {
+ menu->ClearHighlighting(); // cancel highlighting and adjustment
+ menu->Refresh();
+ }
+ mboxActive = true;
+ mboxSeq = mbox->GetSeq();
+ menu->DisplayMessageBox(*mbox);
+ forceRefresh = true;
}
- mboxActive = true;
- mboxSeq = mbox.seq;
- menu->DisplayMessageBox(mbox);
+ }
+ else if (mboxActive)
+ {
+ // Message box has been cancelled from this or another input channel
+ menu->ClearMessageBox();
+ mboxActive = false;
forceRefresh = true;
}
}
- else if (mboxActive)
- {
- // Message box has been cancelled from this or another input channel
- menu->ClearMessageBox();
- mboxActive = false;
- forceRefresh = true;
- }
const uint32_t now = millis();
if (forceRefresh)
@@ -118,12 +168,6 @@ void Display::Spin() noexcept
lastRefreshMillis = now;
}
lcd->FlushSome();
-
- if (beepActive && millis() - whenBeepStarted > beepLength)
- {
- IoPort::WriteAnalog(LcdBeepPin, 0.0, 0);
- beepActive = false;
- }
}
}
@@ -131,11 +175,11 @@ void Display::Exit() noexcept
{
if (lcd != nullptr)
{
- IoPort::WriteAnalog(LcdBeepPin, 0.0, 0); // stop any beep
+ StopBeep();
if (!updatingFirmware)
{
lcd->TextInvert(false);
- lcd->Clear();
+ lcd->ClearAll();
lcd->SetFont(LargeFontNumber);
lcd->SetCursor(20, 0);
lcd->printf("Shutting down...");
@@ -144,29 +188,6 @@ void Display::Exit() noexcept
}
}
-// NOTE: nothing enforces that this beep concludes before another is begun;
-// that is, in rapid succession of commands, only the last beep issued will be heard by the user
-void Display::Beep(unsigned int frequency, unsigned int milliseconds) noexcept
-{
- if (lcd != nullptr)
- {
- whenBeepStarted = millis();
- beepLength = milliseconds;
- beepActive = true;
- IoPort::WriteAnalog(LcdBeepPin, 0.5, (uint16_t)frequency);
- }
-}
-
-void Display::SuccessBeep() noexcept
-{
- Beep(2000, 100);
-}
-
-void Display::ErrorBeep() noexcept
-{
- Beep(500, 1000);
-}
-
void Display::InitDisplay(GCodeBuffer& gb, Lcd *newLcd, Pin csPin, Pin a0Pin, bool defaultCsPolarity) THROWS(GCodeException)
{
// ST7567-based displays need a resistor ratio setting and a contrast setting
@@ -174,14 +195,18 @@ void Display::InitDisplay(GCodeBuffer& gb, Lcd *newLcd, Pin csPin, Pin a0Pin, bo
const uint32_t contrast = (gb.Seen('C')) ? gb.GetUIValue() : DefaultDisplayContrastRatio;
const uint32_t resistorRatio = (gb.Seen('R')) ? gb.GetUIValue() : DefaultDisplayResistorRatio;
newLcd->Init(csPin, a0Pin, defaultCsPolarity, (gb.Seen('F')) ? gb.GetUIValue() : LcdSpiClockFrequency, contrast, resistorRatio);
- IoPort::SetPinMode(LcdBeepPin, OUTPUT_PWM_LOW);
+ StopBeep(); // this serves to initialise the pins
+
newLcd->SetFont(SmallFontNumber);
+#if SUPPORT_ROTARY_ENCODER
if (encoder == nullptr)
{
encoder = new RotaryEncoder(EncoderPinA, EncoderPinB, EncoderPinSw);
encoder->Init(DefaultPulsesPerClick);
}
+#endif
+
menu = new Menu(*newLcd);
menu->Load("main");
lcd = newLcd;
@@ -196,8 +221,12 @@ GCodeResult Display::Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(G
// Delete any existing LCD, menu and encoder
DeleteObject(lcd);
DeleteObject(menu);
+#if SUPPORT_ROTARY_ENCODER
DeleteObject(encoder);
-
+#endif
+#if SUPPORT_RESISTIVE_TOUCH
+ DeleteObject(touchController);
+#endif
seen = true;
switch (gb.GetUIValue())
{
@@ -205,25 +234,48 @@ GCodeResult Display::Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(G
// We have already deleted the display, menu buffer and encoder, so nothing to do here
break;
+#if SUPPORT_12864_LCD
case 1: // 12864 display, ST7920 controller
-#ifdef DUET3MINI
+# ifdef DUET3MINI
// On the Duet 3 Mini we use the A0 pin as CS because it more nearly matches the pinout of the display (with the connectors reversed)
InitDisplay(gb, new Lcd7920(fonts, ARRAY_SIZE(fonts)), LcdA0Pin, NoPin, true);
-#else
+# else
InitDisplay(gb, new Lcd7920(fonts, ARRAY_SIZE(fonts)), LcdCSPin, NoPin, true);
-#endif
+# endif
break;
case 2: // 12864 display, ST7567 controller
-#ifdef DUET_M
+# ifdef DUET_M
// On the Duet Maestro only, the CS pin is active high and gates the clock signal.
// The ST7567 needs an active low CS signal, so we must use a different CS pin and set the original one high to let the clock through.
pinMode(LcdCSPin, OUTPUT_HIGH);
InitDisplay(gb, new Lcd7567(fonts, ARRAY_SIZE(fonts)), LcdCSAltPin, LcdA0Pin, false);
-#else
+# else
InitDisplay(gb, new Lcd7567(fonts, ARRAY_SIZE(fonts)), LcdCSPin, LcdA0Pin, false);
+# endif
+ break;
#endif
+
+#if SUPPORT_ILI9488_LCD
+ case 3: // SPI TFT display with ILI9488 controller
+ SetPinFunction(LcdSpiMosiPin, LcdSpiPinFunction);
+ SetPinFunction(LcdSpiMisoPin, LcdSpiPinFunction);
+ SetPinFunction(LcdSpiSclkPin, LcdSpiPinFunction);
+ SetDriveStrength(LcdSpiMosiPin, 2);
+ SetDriveStrength(LcdSpiSclkPin, 2);
+ pinMode(LcdFlashCsPin, OUTPUT_HIGH); // in case the flash chip is fitted, deselect it
+ pinMode(LcdFontCsPin, OUTPUT_HIGH); // in case the font chip is fitted, deselect it
+ InitDisplay(gb,
+# if USE_FONT_CHIP
+ new LcdILI9488(LcdFontCsPin, LcdSercomNumber),
+# else
+ new LcdILI9488(tftFonts, ARRAY_SIZE(tftFonts), LcdSercomNumber),
+# endif
+ LcdSpiCsPin, NoPin, false);
+ touchController = new ResistiveTouch(RtpSpiCsPin, RtpPenPin);
+ touchController->Init(lcd->GetNumCols(), lcd->GetNumRows(), DisplayOrientation::ReverseY);
break;
+#endif
default:
reply.copy("Unknown display type");
@@ -231,11 +283,13 @@ GCodeResult Display::Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(G
}
}
+#if SUPPORT_ROTARY_ENCODER
if (gb.Seen('E') && encoder != nullptr)
{
seen = true;
encoder->Init(gb.GetIValue()); // configure encoder pulses per click and direction
}
+#endif
if (seen)
{
@@ -245,8 +299,15 @@ GCodeResult Display::Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(G
{
if (lcd != nullptr)
{
- reply.printf("Direct connect display: %s, %.2fMHz, %d encoder pulses per click",
- lcd->GetDisplayTypeName(), (double)(lcd->GetSpiFrequency() * 0.000001), encoder->GetPulsesPerClick());
+ reply.printf("Direct connect display: %s, %.2fMHz"
+#if SUPPORT_ROTARY_ENCODER
+ ", %d encoder pulses per click"
+#endif
+ , lcd->GetDisplayTypeName(), (double)(lcd->GetSpiFrequency() * 0.000001)
+#if SUPPORT_ROTARY_ENCODER
+ , encoder->GetPulsesPerClick()
+#endif
+ );
}
else
{
@@ -262,9 +323,9 @@ void Display::UpdatingFirmware() noexcept
updatingFirmware = true;
if (lcd != nullptr)
{
- IoPort::WriteAnalog(LcdBeepPin, 0.0, 0); // stop any beep
+ StopBeep();
lcd->TextInvert(false);
- lcd->Clear();
+ lcd->ClearAll();
lcd->SetFont(LargeFontNumber);
lcd->SetCursor(20, 0);
lcd->printf("Updating firmware...");
@@ -272,6 +333,57 @@ void Display::UpdatingFirmware() noexcept
}
}
+// NOTE: nothing enforces that this beep concludes before another is begun;
+// that is, in rapid succession of commands, only the last beep issued will be heard by the user
+void Display::Beep(unsigned int frequency, unsigned int milliseconds) noexcept
+{
+ if (lcd != nullptr)
+ {
+#if SUPPORT_12864_LCD
+ IoPort::WriteAnalog(LcdBeepPin, 0.5, (uint16_t)frequency);
+#elif SUPPORT_ILI9488_LCD
+ AnalogOut::Beep(BeeperPins[0], BeeperPins[1], (uint16_t)frequency);
+#endif
+ beepTicksToGo = milliseconds;
+ }
+}
+
+void Display::SuccessBeep() noexcept
+{
+ Beep(2000, 100);
+}
+
+void Display::ErrorBeep() noexcept
+{
+ Beep(500, 1000);
+}
+
+// Caution: this may be called from within an ISR
+void Display::StopBeep() noexcept
+{
+ beepTicksToGo = 0; // do this first to avoid race condition with tick ISR
+#if SUPPORT_12864_LCD
+ IoPort::WriteAnalog(LcdBeepPin, 0.0, 0);
+#elif SUPPORT_ILI9488_LCD
+ AnalogOut::Beep(BeeperPins[0], BeeperPins[1], 0);
+#endif
+}
+
+// This is called from the tick ISR
+void Display::Tick() noexcept
+{
+ uint32_t locTicks = beepTicksToGo; // capture volatile variable to reduce code size
+ if (locTicks != 0)
+ {
+ --locTicks;
+ if (locTicks == 0)
+ {
+ StopBeep();
+ }
+ beepTicksToGo = locTicks;
+ }
+}
+
#endif
// End
diff --git a/src/Display/Display.h b/src/Display/Display.h
index 0263fe90..3f6c9f09 100644
--- a/src/Display/Display.h
+++ b/src/Display/Display.h
@@ -10,9 +10,15 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
+
+#if SUPPORT_ROTARY_ENCODER
+# include "RotaryEncoder.h"
+#endif
+#if SUPPORT_RESISTIVE_TOUCH
+# include "ResistiveTouch.h"
+#endif
-#include "RotaryEncoder.h"
#include "Lcd/Lcd.h"
#include "Menu.h"
#include <ObjectModel/ObjectModel.h>
@@ -23,7 +29,7 @@ public:
Display() noexcept;
void Init() noexcept { }
- GCodeResult Configure(GCodeBuffer& gb, const StringRef& reply);
+ GCodeResult Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException);
void Spin() noexcept;
void Exit() noexcept;
void Beep(unsigned int frequency, unsigned int milliseconds) noexcept;
@@ -31,6 +37,7 @@ public:
void ErrorBeep() noexcept;
bool IsPresent() const noexcept { return lcd != nullptr; }
void UpdatingFirmware() noexcept;
+ void Tick() noexcept;
constexpr static uint8_t DefaultDisplayContrastRatio = 30; // this works well for the Fysetc display
constexpr static uint8_t DefaultDisplayResistorRatio = 6; // the recommended Fysetc display uses 6, some other displays use 3
@@ -39,17 +46,21 @@ protected:
DECLARE_OBJECT_MODEL
private:
- void InitDisplay(GCodeBuffer& gb, Lcd *newLcd, Pin csPin, Pin a0Pin, bool defaultCsPolarity);
+ void InitDisplay(GCodeBuffer& gb, Lcd *newLcd, Pin csPin, Pin a0Pin, bool defaultCsPolarity) THROWS(GCodeException);
+ void StopBeep() noexcept;
- Lcd *lcd;
+ Lcd *null lcd;
Menu *menu;
- RotaryEncoder *encoder;
- uint32_t whenBeepStarted;
- uint32_t beepLength;
+#if SUPPORT_ROTARY_ENCODER
+ RotaryEncoder *null encoder;
+#endif
+#if SUPPORT_RESISTIVE_TOUCH
+ ResistiveTouch *null touchController;
+#endif
+ volatile uint32_t beepTicksToGo;
uint32_t lastRefreshMillis;
uint16_t mboxSeq;
bool mboxActive;
- bool beepActive;
bool updatingFirmware;
};
diff --git a/src/Display/DisplayOrientation.h b/src/Display/DisplayOrientation.h
new file mode 100644
index 00000000..0b10e6d7
--- /dev/null
+++ b/src/Display/DisplayOrientation.h
@@ -0,0 +1,26 @@
+/*
+ * DisplayOrientation.h
+ *
+ * Created: 04/11/2014 17:34:21
+ * Author: David
+ */
+
+
+#ifndef SRC_DISPLAY_DISPLAYORIENTATION_H_
+#define SRC_DISPLAY_DISPLAYORIENTATION_H_
+
+#include <cstdint>
+
+// Enumeration to define the orientation of the display.
+// To keep the code small and fast, we use individual bits to say what needs to be done on the display.
+// Then we define the supported orientations in terms of those bits.
+enum DisplayOrientation : uint8_t {
+ Default = 0x00,
+ SwapXY = 0x01,
+ ReverseX = 0x02,
+ ReverseY = 0x04,
+ InvertText = ReverseY,
+ InvertBitmap = ReverseX
+};
+
+#endif /* SRC_DISPLAY_DISPLAYORIENTATION_H_ */
diff --git a/src/Display/FilesMenuItem.cpp b/src/Display/FilesMenuItem.cpp
new file mode 100644
index 00000000..057003c4
--- /dev/null
+++ b/src/Display/FilesMenuItem.cpp
@@ -0,0 +1,410 @@
+/*
+ * FilesMenuItem.cpp
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#include "FilesMenuItem.h"
+
+#if SUPPORT_DIRECT_LCD && HAS_MASS_STORAGE
+
+FilesMenuItem::FilesMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, const char *_ecv_array cmd, const char *_ecv_array dir, const char *_ecv_array acFile, unsigned int nf) noexcept
+ : MenuItem(r, c, w, LeftAlign, fn), numDisplayLines(nf), command(cmd), initialDirectory(dir), m_acFile(acFile),
+ m_uListingFirstVisibleIndex(0), m_uListingSelectedIndex(0)
+{
+ // There's no guarantee that initialDirectory has a trailing '/'
+ currentDirectory.copy(initialDirectory);
+ const size_t len = currentDirectory.strlen();
+ if (len == 0 || '/' != currentDirectory[len - 1])
+ {
+ currentDirectory.cat('/');
+ }
+
+ initialDirectoryNesting = GetDirectoryNesting();
+ sdCardState = notStarted;
+}
+
+void FilesMenuItem::vResetViewState() noexcept
+{
+ m_uListingSelectedIndex = 0;
+ m_uListingFirstVisibleIndex = 0;
+}
+
+void FilesMenuItem::EnterDirectory() noexcept
+{
+ vResetViewState();
+
+ m_uHardItemsInDirectory = 0;
+ FileInfo oFileInfo;
+ if (MassStorage::FindFirst(currentDirectory.c_str(), oFileInfo))
+ {
+ do
+ {
+ if (oFileInfo.fileName[0] != '.')
+ {
+ ++m_uHardItemsInDirectory;
+ }
+ }
+ while (MassStorage::FindNext(oFileInfo));
+ }
+
+ itemChanged = true; // force a redraw
+}
+
+uint8_t FilesMenuItem::GetDirectoryNesting() const noexcept
+{
+ const char *pcPathElement = currentDirectory.c_str();
+ uint8_t uNumSlashes = 0;
+
+ while ('\0' != *pcPathElement)
+ {
+ if (('/' == *pcPathElement) && ('\0' != *(pcPathElement + 1))) // don't count a trailing slash
+ {
+ ++uNumSlashes;
+ }
+ ++pcPathElement;
+ }
+ return uNumSlashes;
+}
+
+bool FilesMenuItem::bInSubdirectory() const noexcept
+{
+ return GetDirectoryNesting() > initialDirectoryNesting;
+}
+
+unsigned int FilesMenuItem::uListingEntries() const noexcept
+{
+ return bInSubdirectory() ? (1 + m_uHardItemsInDirectory) : m_uHardItemsInDirectory;
+}
+
+void FilesMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
+{
+ // The 'highlight' parameter is not used to highlight this item, but it is still used to tell whether this item is selected or not
+ if (!IsVisible())
+ {
+ sdCardState = notStarted;
+ }
+ else if (!drawn || itemChanged || highlighted != highlight)
+ {
+ switch (sdCardState)
+ {
+ case notStarted:
+ if (MassStorage::CheckDriveMounted(currentDirectory.c_str()))
+ {
+ sdCardState = mounted;
+ EnterDirectory();
+ }
+ else
+ {
+ sdCardState = mounting;
+ }
+ break;
+
+ case mounting:
+ {
+ const size_t card = (isdigit(currentDirectory[0]) && currentDirectory[1] == ':') ? currentDirectory[0] - '0' : 0;
+ String<StringLength50> reply;
+ switch(MassStorage::Mount(card, reply.GetRef(), false))
+ {
+ case GCodeResult::notFinished:
+ return;
+
+ case GCodeResult::ok:
+ sdCardState = mounted;
+ EnterDirectory();
+ break;
+
+ default:
+ reply.copy("Internal error");
+ // no break
+ case GCodeResult::error:
+ sdCardState = error;
+ lcd.SetFont(fontNumber);
+ lcd.SetCursor(row, column);
+ lcd.SetRightMargin(rightMargin);
+ lcd.ClearToMargin();
+ lcd.SetCursor(row, column);
+ lcd.printf("%s", reply.c_str());
+ break;
+ }
+ }
+ break;
+
+ case mounted:
+ ListFiles(lcd, rightMargin, highlight);
+ break;
+
+ case error:
+ break;
+ }
+ }
+}
+
+void FilesMenuItem::ListFiles(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
+{
+ lcd.SetFont(fontNumber);
+ lcd.SetRightMargin(rightMargin);
+ uint8_t line = 0;
+
+ // If we are in a subdirectory then we prepend ".." to the list of files
+ unsigned int dirEntriesToSkip;
+ if (bInSubdirectory())
+ {
+ if (m_uListingFirstVisibleIndex == 0)
+ {
+ lcd.SetCursor(row, column);
+ lcd.printf(" ..");
+ lcd.ClearToMargin();
+ if (highlight && m_uListingSelectedIndex == 0)
+ {
+ // Overwriting the initial spaces with '>' avoids shifting the following text when we change the selection
+ lcd.SetCursor(row, column);
+ lcd.write('>');
+ }
+ line = 1;
+ dirEntriesToSkip = 0;
+ }
+ else
+ {
+ dirEntriesToSkip = m_uListingFirstVisibleIndex - 1;
+ }
+ }
+ else
+ {
+ dirEntriesToSkip = m_uListingFirstVisibleIndex;
+ }
+
+ // Seek to the first file that is in view
+ FileInfo oFileInfo;
+ bool gotFileInfo = MassStorage::FindFirst(currentDirectory.c_str(), oFileInfo);
+ while (gotFileInfo)
+ {
+ if (oFileInfo.fileName[0] != '.')
+ {
+ if (dirEntriesToSkip == 0)
+ {
+ break;
+ }
+ --dirEntriesToSkip;
+ }
+ gotFileInfo = MassStorage::FindNext(oFileInfo);
+ }
+
+ // We always iterate the entire viewport so that old listing lines that may not be overwritten are cleared
+ while (line < numDisplayLines)
+ {
+ lcd.SetCursor(row + (lcd.GetFontHeight() * line), column);
+
+ // If there's actually a file to describe (not just ensuring viewport line clear)
+ if (gotFileInfo)
+ {
+ lcd.printf(" ");
+ if (oFileInfo.isDirectory)
+ {
+ lcd.printf("./");
+ }
+ lcd.printf("%s", oFileInfo.fileName.c_str());
+ lcd.ClearToMargin();
+ if (highlight && m_uListingSelectedIndex == line + m_uListingFirstVisibleIndex)
+ {
+ lcd.SetCursor(row + (lcd.GetFontHeight() * line), column);
+ lcd.write('>');
+ }
+ }
+ else
+ {
+ lcd.ClearToMargin();
+ }
+
+ ++line;
+ if (line == numDisplayLines)
+ {
+ break; // skip getting more file info for efficiency
+ }
+
+ do
+ {
+ gotFileInfo = MassStorage::FindNext(oFileInfo);
+ } while (gotFileInfo && oFileInfo.fileName[0] == '.');
+ }
+
+ MassStorage::AbandonFindNext(); // release the mutex, there may be more files that we don't have room to display
+
+ itemChanged = false;
+ drawn = true;
+ highlighted = highlight;
+}
+
+void FilesMenuItem::Enter(bool bForwardDirection) noexcept
+{
+ if (bForwardDirection || uListingEntries() == 0)
+ {
+ m_uListingSelectedIndex = 0;
+ m_uListingFirstVisibleIndex = 0; // select the first item and start the list from the first item
+ }
+ else
+ {
+ m_uListingSelectedIndex = uListingEntries() - 1; // select the last item
+ m_uListingFirstVisibleIndex = ((uListingEntries() > numDisplayLines) ? (uListingEntries() - numDisplayLines) : 0);
+ }
+ itemChanged = true;
+}
+
+int FilesMenuItem::Advance(int nCounts) noexcept
+{
+ // In case of empty directory, there's nothing the control itself can do
+ if (uListingEntries() != 0)
+ {
+ while (nCounts > 0)
+ {
+ // Advancing one more would take us past the end of the list
+ // Instead, return the remaining count so that the other selectable menu items can be scrolled.
+ if (m_uListingSelectedIndex + 1 == uListingEntries())
+ {
+ break;
+ }
+
+ ++m_uListingSelectedIndex;
+ --nCounts;
+
+ // Move the visible portion of the list down, if required
+ if (m_uListingSelectedIndex == m_uListingFirstVisibleIndex + numDisplayLines)
+ {
+ ++m_uListingFirstVisibleIndex;
+ }
+ }
+
+ while (nCounts < 0)
+ {
+ // We're already at the first item in the visible list; return the remaining action to the menu system.
+ if (0 == m_uListingSelectedIndex)
+ {
+ break;
+ }
+
+ --m_uListingSelectedIndex;
+ ++nCounts;
+
+ // Move the visible portion of the list up, if required
+ if (m_uListingSelectedIndex < m_uListingFirstVisibleIndex)
+ {
+ --m_uListingFirstVisibleIndex;
+ }
+ }
+
+ itemChanged = true;
+ }
+
+ return nCounts;
+}
+
+bool FilesMenuItem::Select(const StringRef& cmd) noexcept
+{
+ // Several cases:
+ // TEST 1. ".." entry - call EnterDirectory(), using saved state information
+ // TEST 2. Directory - call EnterDirectory(), adding to saved state information
+ // 3. File - run command with filename as argument
+
+ // Get information on the item selected
+
+ if (bInSubdirectory() && 0 == m_uListingSelectedIndex) // meaning ".."
+ {
+ // TODO: go up one level rather than to logical root
+ // There's no guarantee that initialDirectory has a trailing '/'
+ currentDirectory.copy(initialDirectory);
+ const size_t len = currentDirectory.strlen();
+ if (len == 0 || '/' != currentDirectory[len - 1])
+ {
+ currentDirectory.cat('/');
+ }
+ EnterDirectory();
+ }
+ else
+ {
+ // If subdir:
+ // If ".." is visible, the selected file is visible index m_uListingSelectedIndex, fs index m_uListingSelectedIndex - 1
+ // If ".." is not visible, the selected file is visible index m_uListingSelectedIndex, fs index m_uListingSelectedIndex - 1
+ // If logical root:
+ // ".." is never visible, so the selected file is visible index m_uListingSelectedIndex, fs index m_uListingSelectedIndex
+ unsigned int dirEntriesToSkip = bInSubdirectory() ? m_uListingSelectedIndex - 1 : m_uListingSelectedIndex;
+
+ // Seek to the selected file
+ FileInfo oFileInfo;
+ bool gotFileInfo = MassStorage::FindFirst(currentDirectory.c_str(), oFileInfo);
+ while (gotFileInfo)
+ {
+ if (oFileInfo.fileName[0] != '.')
+ {
+ if (dirEntriesToSkip == 0)
+ {
+ break;
+ }
+ --dirEntriesToSkip;
+ }
+ gotFileInfo = MassStorage::FindNext(oFileInfo);
+ }
+ MassStorage::AbandonFindNext();
+
+ if (gotFileInfo) // handles empty directory (no action)
+ {
+ if (oFileInfo.isDirectory)
+ {
+ // Build the new directory, and ensure it's terminated with a forward slash
+ currentDirectory.cat(oFileInfo.fileName.c_str());
+ currentDirectory.cat('/');
+ EnterDirectory();
+ }
+ else
+ {
+ int nReplacementIndex = StringContains(command, "#0");
+ if (nReplacementIndex != -1)
+ {
+ cmd.copy(command, nReplacementIndex);
+ cmd.cat('"');
+ cmd.cat(currentDirectory.c_str());
+ cmd.cat(oFileInfo.fileName.c_str());
+ cmd.cat('"');
+ cmd.cat(command + nReplacementIndex + strlen("#0"));
+ }
+ else
+ {
+ cmd.copy(command);
+ }
+
+ // TODO: do this on the way in and it might be less work...
+ // On the other hand, this only occurs when an item is selected so it's O(1) vs. O(n)
+ nReplacementIndex = cmd.Contains("menu");
+ if (nReplacementIndex != -1)
+ {
+ cmd.Truncate(nReplacementIndex);
+ cmd.cat(m_acFile);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void FilesMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
+{
+ // The width is always set for a FilesMenuItem so we just need to determine the height
+ if (height == 0)
+ {
+ lcd.SetFont(fontNumber);
+ height = lcd.GetFontHeight() * numDisplayLines;
+ }
+}
+
+PixelNumber FilesMenuItem::GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept
+{
+ // TODO
+ return 0;
+}
+
+#endif
+
+// End
diff --git a/src/Display/FilesMenuItem.h b/src/Display/FilesMenuItem.h
new file mode 100644
index 00000000..1166acad
--- /dev/null
+++ b/src/Display/FilesMenuItem.h
@@ -0,0 +1,63 @@
+/*
+ * FilesMenuItem.h
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_FILESMENUITEM_H_
+#define SRC_DISPLAY_FILESMENUITEM_H_
+
+#include "MenuItem.h"
+
+#if SUPPORT_DIRECT_LCD && HAS_MASS_STORAGE
+
+class FilesMenuItem final : public MenuItem
+{
+public:
+ DECLARE_FREELIST_NEW_DELETE(FilesMenuItem)
+
+ FilesMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, const char *_ecv_array cmd, const char *_ecv_array dir, const char *_ecv_array acFile, unsigned int nf) noexcept;
+ void Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept override;
+ void Enter(bool bForwardDirection) noexcept override;
+ int Advance(int nCounts) noexcept override;
+ bool Select(const StringRef& cmd) noexcept override;
+ void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
+
+ PixelNumber GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept override;
+
+ void EnterDirectory() noexcept;
+
+protected:
+ void vResetViewState() noexcept;
+
+private:
+ void ListFiles(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept;
+ uint8_t GetDirectoryNesting() const noexcept;
+
+ const unsigned int numDisplayLines;
+
+ const char *_ecv_array command;
+ const char *_ecv_array initialDirectory;
+ const char *_ecv_array m_acFile; // used when action ("command") includes "menu"
+
+ // Working
+ String<MaxFilenameLength> currentDirectory;
+
+ bool bInSubdirectory() const noexcept;
+ unsigned int uListingEntries() const noexcept;
+
+ // Files on the file system, real count i.e. no ".." included
+ unsigned int m_uHardItemsInDirectory;
+
+ // Logical items (c. files) for display, referenced to uListingEntries() count
+ unsigned int m_uListingFirstVisibleIndex;
+ unsigned int m_uListingSelectedIndex;
+
+ enum CardState : uint8_t { notStarted, mounting, mounted, error } sdCardState;
+ uint8_t initialDirectoryNesting;
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_FILESMENUITEM_H_ */
diff --git a/src/Display/ImageMenuItem.cpp b/src/Display/ImageMenuItem.cpp
new file mode 100644
index 00000000..e97c88d6
--- /dev/null
+++ b/src/Display/ImageMenuItem.cpp
@@ -0,0 +1,78 @@
+/*
+ * ImageMenuItem.cpp
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#include "ImageMenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+#include <Platform/RepRap.h>
+#include <Platform/Platform.h>
+
+// Image menu item members
+// The image file format is:
+// Byte 0 = number of columns
+// Byte 1 = number of rows
+// Remaining bytes = data, 1 row at a time. If the number of columns is not a multiple of 8 then the data for each row is padded to a multiple of 8 bits.
+ImageMenuItem::ImageMenuItem(PixelNumber r, PixelNumber c, const char *_ecv_array pFileName) noexcept
+ : MenuItem(r, c, 0, 0, 0)
+{
+ fileName.copy(pFileName);
+}
+
+void ImageMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
+{
+ if (IsVisible() && (!drawn || itemChanged || highlight != highlighted))
+ {
+ FileStore *_ecv_null const fs = reprap.GetPlatform().OpenFile(MENU_DIR, fileName.c_str(), OpenMode::read);
+ if (fs != nullptr)
+ {
+ uint8_t widthAndHeight[2];
+ if (fs->Read(widthAndHeight, 2) == 2)
+ {
+ const PixelNumber cols = widthAndHeight[0];
+ const PixelNumber rows = widthAndHeight[1];
+ if (cols != 0 && cols <= lcd.GetNumCols() && rows != 0)
+ {
+ const size_t bytesPerRow = (cols + 7)/8;
+ for (PixelNumber irow = 0; irow < rows; ++irow)
+ {
+ uint8_t buffer[lcd.GetNumCols()/8];
+ if (fs->Read(buffer, bytesPerRow) != (int)bytesPerRow)
+ {
+ break;
+ }
+ lcd.BitmapRow(row + irow, column, cols, buffer, highlight);
+ }
+ }
+ }
+ fs->Close();
+ }
+ itemChanged = false;
+ drawn = true;
+ highlighted = highlight;
+ }
+}
+
+void ImageMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
+{
+ if (width == 0 || height == 0)
+ {
+ FileStore * const fs = reprap.GetPlatform().OpenFile(MENU_DIR, fileName.c_str(), OpenMode::read);
+ if (fs != nullptr)
+ {
+ uint8_t w[2];
+ fs->Read(w, 2); // read the number of columns
+ fs->Close();
+ width = w[0];
+ height = w[1];
+ }
+ }
+}
+
+#endif
+
+// End
diff --git a/src/Display/ImageMenuItem.h b/src/Display/ImageMenuItem.h
new file mode 100644
index 00000000..fa301190
--- /dev/null
+++ b/src/Display/ImageMenuItem.h
@@ -0,0 +1,31 @@
+/*
+ * ImageMenuItem.h
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_IMAGEMENUITEM_H_
+#define SRC_DISPLAY_IMAGEMENUITEM_H_
+
+#include "MenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+class ImageMenuItem final : public MenuItem
+{
+public:
+ DECLARE_FREELIST_NEW_DELETE(ImageMenuItem)
+
+ ImageMenuItem(PixelNumber r, PixelNumber c, const char *_ecv_array pFileName) noexcept;
+
+ void Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept override;
+ void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
+
+private:
+ String<MaxFilenameLength> fileName;
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_IMAGEMENUITEM_H_ */
diff --git a/src/Display/Lcd/Fonts/ER3301_1.cpp b/src/Display/Lcd/Fonts/ER3301_1.cpp
new file mode 100644
index 00000000..d3296294
--- /dev/null
+++ b/src/Display/Lcd/Fonts/ER3301_1.cpp
@@ -0,0 +1,199 @@
+/*
+ * ER3301_1.cpp
+ *
+ * Created on: 27 May 2022
+ * Author: David
+ *
+ * Support for ER3303-1 font chip included on some variants of the ER-TFTM035-6 display
+ */
+
+#include "ER3301_1.h"
+
+#if USE_FONT_CHIP
+
+#include <Hardware/Spi/SharedSpiDevice.h>
+
+// This font chip contains several fonts. We are only interested in the 12-dot and 16 dot high Unicode Latin/Greek Cyrillic and Arabic fonts.
+// We number those as fonts 0 and 1. Fonts 2 and 3 are the same ones expanded to double size.
+
+struct FontTableEntry
+{
+ uint16_t startCharacter;
+ uint16_t endCharacter;
+ uint32_t startAddress;
+};
+
+struct FontDescriptor
+{
+ const FontTableEntry *tableStart;
+ const FontTableEntry *tableEnd;
+ uint16_t height;
+ uint16_t spaceWidth;
+ unsigned int bytesPerCharacter;
+};
+
+static constexpr FontTableEntry Font12[] =
+{
+ { 0x0020, 0x007F, 0x19AD22 }, // Latin
+ { 0x00A0, 0x017F, 0x19AD22 + 96 * 26}, // Latin
+ { 0x0384, 0x03CE, 0x19AD22 + 350 * 26 }, // Greek
+ { 0x0400, 0x045F, 0x19AD22 + 425 * 26 }, // Cyrillic
+ { 0x0490, 0x04A3, 0x19AD22 + (425 + 96) * 26 }, // Cyrillic
+ { 0x04AE, 0x04B3, 0x19AD22 + (425 + 117) * 26 }, // Cyrillic
+ { 0x04B8, 0x04BB, 0x19AD22 + (425 + 122) * 26 }, // Cyrillic
+ { 0x04D8, 0x04D9, 0x19AD22 + (425 + 126) * 26 }, // Cyrillic
+ { 0x04E8, 0x04E9, 0x19AD22 + (425 + 128) * 26 }, // Cyrillic
+ { 0x0600, 0x06F9, 0x1AA0E6 } // Arabic
+};
+
+static constexpr FontTableEntry Font16[] =
+{
+ { 0x0020, 0x007F, 0x19E580 }, // Latin
+ { 0x00A0, 0x017F, 0x19E580 + 96 * 34}, // Latin
+ { 0x0384, 0x03CE, 0x19E580 + 350 * 34 }, // Greek
+ { 0x0400, 0x045F, 0x19E580 + 425 * 34 }, // Cyrillic
+ { 0x0490, 0x04A3, 0x19E580 + (425 + 96) * 34 }, // Cyrillic
+ { 0x04AE, 0x04B3, 0x19E580 + (425 + 117) * 34 }, // Cyrillic
+ { 0x04B8, 0x04BB, 0x19E580 + (425 + 122) * 34 }, // Cyrillic
+ { 0x04D8, 0x04D9, 0x19E580 + (425 + 126) * 34 }, // Cyrillic
+ { 0x04E8, 0x04E9, 0x19E580 + (425 + 128) * 34 }, // Cyrillic
+ { 0x0600, 0x06F9, 0x1A2F36 } // Arabic
+};
+
+static constexpr FontDescriptor Fonts[] =
+{
+ { Font12, Font12 + ARRAY_SIZE(Font12), 12, 1, 26 },
+ { Font16, Font16 + ARRAY_SIZE(Font16), 16, 1, 34 },
+};
+
+constexpr bool FontSizeOK(const FontDescriptor *descriptor, size_t numFonts) noexcept
+{
+ return descriptor->bytesPerCharacter <= ER3301_1::MaxBytesPerCharacter && (numFonts == 1 || FontSizeOK(descriptor + 1, numFonts - 1));
+}
+
+static_assert(FontSizeOK(Fonts, ARRAY_SIZE(Fonts)));
+
+// Search for a character in the font table, returning its address of found, 0 if not found
+static uint32_t LookupCharacter(uint16_t codePoint, const FontDescriptor& font) noexcept
+{
+ const FontTableEntry *table = font.tableStart;
+ do
+ {
+ if (codePoint <= table->endCharacter)
+ {
+ if (codePoint >= table->startCharacter)
+ {
+ return table->startAddress + ((codePoint - table->startCharacter) * font.bytesPerCharacter);
+ }
+ break;
+ }
+ ++table;
+ } while (table != font.tableEnd);
+ return 0;
+}
+
+ER3301_1::ER3301_1(Pin csPin) noexcept
+ : spiClient(SharedSpiDevice::GetMainSharedSpiDevice(), SpiFrequency, SpiMode::mode0, csPin, false), currentAddress(0)
+{
+}
+
+// Return the count of available fonts
+unsigned int ER3301_1::GetNumFonts() const noexcept
+{
+ return 2 * ARRAY_SIZE(Fonts);
+}
+
+// Return the height of the specified font
+uint16_t ER3301_1::GetFontHeight(unsigned int fontNumber) const noexcept
+{
+ const uint16_t nativeFontHeight = Fonts[fontNumber % ARRAY_SIZE(Fonts)].height;
+ return (fontNumber >= ARRAY_SIZE(Fonts)) ? 2 * nativeFontHeight : nativeFontHeight;
+}
+
+// Get the inter-character spacing in the specified font
+uint16_t ER3301_1::GetSpacing(unsigned int fontNumber) const noexcept
+{
+ const uint16_t nativeSpacing = Fonts[fontNumber % ARRAY_SIZE(Fonts)].spaceWidth;
+ return (fontNumber >= ARRAY_SIZE(Fonts)) ? 2 * nativeSpacing : nativeSpacing;
+}
+
+// Fetch the data for the next column and advance the column counter
+uint32_t ER3301_1::GetColumnData(uint16_t column) noexcept
+{
+ uint32_t ret = 0;
+ if (doubleSize)
+ {
+ for (unsigned int row = 0; row < nativeHeight; ++row)
+ {
+ const uint16_t data = __builtin_bswap16(charBuffer.data[row]);
+ if ((data << ((column >> 1) + columnOffset)) & 0x8000) // if pixel is set
+ {
+ ret |= 3u << (row << 1);
+ }
+ }
+ }
+ else
+ {
+ for (unsigned int row = 0; row < nativeHeight; ++row)
+ {
+ const uint16_t data = __builtin_bswap16(charBuffer.data[row]);
+ if ((data << (column + columnOffset)) & 0x8000) // if pixel is set
+ {
+ ret |= 1u << row;
+ }
+ }
+ }
+ return ret;
+}
+
+// Fetch the specified character into the buffer, set nextColumn to zero and return its width
+uint16_t ER3301_1::GetCharacter(unsigned int fontNumber, uint16_t codePoint) noexcept
+{
+ uint32_t addr = LookupCharacter(codePoint, Fonts[fontNumber % ARRAY_SIZE(Fonts)]);
+ if (addr == 0)
+ {
+ addr = LookupCharacter(0x007F, Fonts[fontNumber % ARRAY_SIZE(Fonts)]);
+ if (addr == 0)
+ {
+ return 0;
+ }
+ }
+ if (addr != currentAddress)
+ {
+ // Fetch the character from the font chip
+ spiClient.Select();
+ charBuffer.cmd = 0x03;
+ charBuffer.addr[0] = (uint8_t)(addr >> 16);
+ charBuffer.addr[1] = (uint8_t)(addr >> 8);
+ charBuffer.addr[2] = (uint8_t)addr;
+ spiClient.TransceivePacket((const uint8_t *)&charBuffer, (uint8_t *)&charBuffer, 4 + Fonts[fontNumber % ARRAY_SIZE(Fonts)].bytesPerCharacter);
+ spiClient.Deselect();
+
+ currentAddress = addr;
+ nativeHeight = Fonts[fontNumber % ARRAY_SIZE(Fonts)].height; // this is the native height
+ nativeWidth = __builtin_bswap16(charBuffer.width);
+
+ // Check whether there is a leading blank column that we need to remove
+ uint16_t pixels = 0;
+ for (unsigned int row = 0; row < nativeHeight; ++row)
+ {
+ pixels |= charBuffer.data[row];
+ }
+ if ((pixels & 0x0080) != 0 && nativeWidth != 0) // use 0x0080 instead of 0x8000 because we didn't swap the bytes
+ {
+ columnOffset = 0;
+ }
+ else
+ {
+ // Remove the leading space column
+ --nativeWidth;
+ columnOffset = 1;
+ }
+ }
+ doubleSize = (fontNumber >= ARRAY_SIZE(Fonts));
+ return (doubleSize) ? 2 * nativeWidth : nativeWidth;
+}
+
+#endif
+
+// End
diff --git a/src/Display/Lcd/Fonts/ER3301_1.h b/src/Display/Lcd/Fonts/ER3301_1.h
new file mode 100644
index 00000000..ee999aa9
--- /dev/null
+++ b/src/Display/Lcd/Fonts/ER3301_1.h
@@ -0,0 +1,62 @@
+/*
+ * ER3301_1.h
+ *
+ * Created on: 27 May 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_LCD_FONTS_ER3301_1_H_
+#define SRC_DISPLAY_LCD_FONTS_ER3301_1_H_
+
+#include <RepRapFirmware.h>
+
+#if USE_FONT_CHIP
+
+#include <Hardware/Spi/SharedSpiClient.h>
+
+class ER3301_1
+{
+public:
+ ER3301_1(Pin csPin) noexcept;
+
+ // Return the count of available fonts
+ unsigned int GetNumFonts() const noexcept;
+
+ // Return the height of the specified font
+ uint16_t GetFontHeight(unsigned int fontNumber) const noexcept;
+
+ // Get the inter-character spacing in the specified font
+ uint16_t GetSpacing(unsigned int fontNumber) const noexcept;
+
+ // Fetch the specified character into the buffer and return its width
+ uint16_t GetCharacter(unsigned int fontNumber, uint16_t codePoint) noexcept;
+
+ // Fetch the data for the next column and advance the column counter
+ uint32_t GetColumnData(uint16_t column) noexcept;
+
+ static constexpr size_t MaxBytesPerCharacter = 34;
+
+private:
+ static constexpr uint32_t SpiFrequency = 4000000; // the SPI frequency we use (max supported is 20MHz in Read mode, 30MHz in FastRead mode)
+
+ SharedSpiClient spiClient;
+ uint32_t currentAddress; // the address for which we have a character in the buffer
+ uint16_t nativeHeight; // the native height of the character in the buffer
+ uint16_t nativeWidth; // the width of the character in the buffer, after removing a leading space if we need to
+ uint16_t columnOffset; // the number of words of leading spaces we need to skip (we need to manage spaces ourselves to do auto kerning)
+ bool doubleSize; // true if the current font scales the size by two
+
+ struct CharData
+ {
+ uint8_t cmd; // space for the Read command
+ uint8_t addr[3]; // the 3-byte address we want, MSB first
+ uint16_t width; // the width of the character
+ uint16_t data[MaxBytesPerCharacter/2 - 1]; // the character data, one word per row
+ };
+
+ CharData charBuffer; // buffer to hold the SPI command and the fetched font data
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_LCD_FONTS_ER3301_1_H_ */
diff --git a/src/Display/Lcd/Fonts/glcd11x14.cpp b/src/Display/Lcd/Fonts/glcd11x14.cpp
index 4f7e9235..575805ec 100644
--- a/src/Display/Lcd/Fonts/glcd11x14.cpp
+++ b/src/Display/Lcd/Fonts/glcd11x14.cpp
@@ -7,7 +7,7 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
#include "LcdFont.h"
diff --git a/src/Display/Lcd/Fonts/glcd19x21.cpp b/src/Display/Lcd/Fonts/glcd19x21.cpp
new file mode 100644
index 00000000..c976c8a0
--- /dev/null
+++ b/src/Display/Lcd/Fonts/glcd19x21.cpp
@@ -0,0 +1,549 @@
+/*
+ * glcd19x21_new.cpp
+ *
+ * Created: 04/03/2015 13:25:32
+ * Author: David
+ */
+
+//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0
+//MikroElektronika 2011
+//http://www.mikroe.com
+
+//GLCD FontName : Liberation_Sans19x21
+//GLCD FontSize : 19 x 21
+
+#include "RepRapFirmware.h"
+
+#if SUPPORT_DIRECT_LCD
+
+#include "LcdFont.h"
+
+#define USE_CYRILLIC_CHARACTERS 0
+
+const uint8_t Liberation_Sans19x21[] =
+{
+ // ASCII 0x20 to 0x7F
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x02, 0xF8, 0x9F, 0x01, 0xF8, 0x9F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
+ 0x05, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
+ 0x0C, 0x00, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0xF0, 0x01, 0x80, 0x1F, 0x00, 0xF8, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0xF0, 0x01, 0x80, 0x1F, 0x00, 0xF8, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char #
+ 0x0C, 0x00, 0x20, 0x00, 0xE0, 0xE0, 0x00, 0xF0, 0xC1, 0x00, 0x18, 0x83, 0x01, 0x08, 0x02, 0x01, 0xFE, 0xFF, 0x07, 0xFE, 0xFF, 0x07, 0x08, 0x06, 0x01, 0x18, 0x04, 0x01, 0x30, 0x8C, 0x00, 0x20, 0xF8, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $
+ 0x11, 0xF0, 0x03, 0x00, 0x18, 0x06, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x01, 0x18, 0x86, 0x01, 0xF0, 0xE3, 0x00, 0x00, 0x30, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x06, 0x00, 0x80, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x70, 0xFC, 0x00, 0x18, 0x86, 0x01, 0x08, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x86, 0x01, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char %
+ 0x0D, 0x00, 0xF8, 0x00, 0x00, 0xDC, 0x00, 0x60, 0x86, 0x01, 0xF0, 0x03, 0x01, 0x18, 0x07, 0x01, 0x08, 0x1D, 0x01, 0x08, 0xB9, 0x01, 0x98, 0xE0, 0x00, 0xF0, 0xE0, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char &
+ 0x02, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
+ 0x05, 0x00, 0x3E, 0x00, 0xC0, 0xFF, 0x01, 0xF0, 0x80, 0x07, 0x18, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
+ 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x0C, 0xF0, 0x80, 0x07, 0xC0, 0xFF, 0x01, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char )
+ 0x06, 0x20, 0x00, 0x00, 0x40, 0x03, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x40, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char *
+ 0x0A, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xE0, 0x3F, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char +
+ 0x02, 0x00, 0x80, 0x09, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
+ 0x05, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char -
+ 0x02, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
+ 0x06, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x01, 0x00, 0x3F, 0x00, 0xE0, 0x07, 0x00, 0x7C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char /
+ 0x0A, 0xC0, 0x3F, 0x00, 0xF0, 0xFF, 0x00, 0x10, 0x80, 0x00, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x10, 0x80, 0x00, 0xF0, 0xFF, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0
+ 0x09, 0x60, 0x00, 0x01, 0x30, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1
+ 0x0A, 0x40, 0x80, 0x01, 0x70, 0xC0, 0x01, 0x10, 0x60, 0x01, 0x08, 0x30, 0x01, 0x08, 0x18, 0x01, 0x08, 0x08, 0x01, 0x08, 0x0C, 0x01, 0x18, 0x07, 0x01, 0xF0, 0x03, 0x01, 0xC0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2
+ 0x0A, 0x60, 0x60, 0x00, 0x70, 0xE0, 0x00, 0x18, 0x80, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x18, 0x85, 0x01, 0xF0, 0xFD, 0x00, 0x60, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3
+ 0x0B, 0x00, 0x10, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x13, 0x00, 0x80, 0x11, 0x00, 0xE0, 0x10, 0x00, 0x70, 0x10, 0x00, 0x18, 0x10, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4
+ 0x0A, 0x80, 0x63, 0x00, 0xF8, 0xE3, 0x00, 0x08, 0x82, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x01, 0x08, 0x83, 0x01, 0x08, 0xFE, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5
+ 0x0A, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x30, 0xC2, 0x00, 0x18, 0x81, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x01, 0x08, 0x01, 0x01, 0x18, 0x83, 0x01, 0x30, 0xFE, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6
+ 0x0A, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0xE0, 0x01, 0x08, 0xFC, 0x01, 0x08, 0x0F, 0x00, 0xC8, 0x03, 0x00, 0xE8, 0x00, 0x00, 0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7
+ 0x0A, 0xE0, 0x78, 0x00, 0xF0, 0xFD, 0x00, 0x18, 0x85, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x18, 0x85, 0x01, 0xF0, 0xFD, 0x00, 0xE0, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8
+ 0x0A, 0xE0, 0x43, 0x00, 0xF0, 0xC7, 0x00, 0x18, 0x8C, 0x01, 0x08, 0x08, 0x01, 0x08, 0x08, 0x01, 0x08, 0x08, 0x01, 0x08, 0x88, 0x01, 0x30, 0xC4, 0x00, 0xF0, 0x7F, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9
+ 0x02, 0xC0, 0x80, 0x01, 0xC0, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
+ 0x02, 0xC0, 0x80, 0x09, 0xC0, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
+ 0x0A, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x80, 0x0D, 0x00, 0x80, 0x0D, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0x40, 0x10, 0x00, 0x60, 0x30, 0x00, 0x20, 0x20, 0x00, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char <
+ 0x0A, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char =
+ 0x0A, 0x30, 0x60, 0x00, 0x20, 0x20, 0x00, 0x60, 0x30, 0x00, 0x40, 0x10, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0x80, 0x0D, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
+ 0x0A, 0x60, 0x00, 0x00, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x98, 0x01, 0x08, 0x9C, 0x01, 0x08, 0x06, 0x00, 0x18, 0x02, 0x00, 0xF0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ?
+ 0x11, 0x00, 0xFF, 0x00, 0xC0, 0x81, 0x03, 0x60, 0x00, 0x02, 0x10, 0x18, 0x04, 0x18, 0x7F, 0x0C, 0x88, 0xC3, 0x08, 0xC4, 0x80, 0x08, 0x44, 0x80, 0x08, 0x44, 0x80, 0x08, 0x44, 0x40, 0x08, 0x84, 0x30, 0x08, 0x04, 0xFF, 0x04, 0xCC, 0x87, 0x04, 0x48, 0x80, 0x06, 0x18, 0xC0, 0x00, 0x70, 0x70, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char @
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0xE0, 0x0B, 0x00, 0x78, 0x08, 0x00, 0x18, 0x08, 0x00, 0x78, 0x08, 0x00, 0xE0, 0x0B, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x18, 0x03, 0x01, 0xF0, 0x85, 0x00, 0xF0, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B
+ 0x0D, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0xC0, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x18, 0x80, 0x01, 0x30, 0x80, 0x00, 0x70, 0xE0, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C
+ 0x0C, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x18, 0x80, 0x01, 0x10, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E
+ 0x0A, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F
+ 0x0D, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x04, 0x01, 0x08, 0x04, 0x01, 0x18, 0x04, 0x01, 0x30, 0x84, 0x00, 0x70, 0xC4, 0x00, 0x60, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G
+ 0x0A, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H
+ 0x02, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
+ 0x09, 0x00, 0x20, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x80, 0x01, 0xF8, 0xFF, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x80, 0x07, 0x00, 0xC0, 0x1C, 0x00, 0x60, 0x38, 0x00, 0x30, 0x70, 0x00, 0x18, 0xE0, 0x00, 0x08, 0xC0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K
+ 0x09, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L
+ 0x0D, 0xF8, 0xFF, 0x01, 0x18, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x00, 0x00, 0x3E, 0x00, 0x80, 0x07, 0x00, 0xF0, 0x00, 0x00, 0x18, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M
+ 0x0A, 0xF8, 0xFF, 0x01, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x38, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xC0, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x18, 0x80, 0x01, 0x10, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x18, 0x06, 0x00, 0xF0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x0F, 0x08, 0x00, 0x0D, 0x18, 0x80, 0x19, 0x10, 0x80, 0x10, 0x70, 0xE0, 0x10, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q
+ 0x0C, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x0C, 0x00, 0x08, 0x1C, 0x00, 0x18, 0x76, 0x00, 0xF0, 0xE3, 0x01, 0xF0, 0x83, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char R
+ 0x0C, 0x00, 0x60, 0x00, 0xF0, 0xE1, 0x00, 0xF0, 0x81, 0x00, 0x18, 0x83, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x06, 0x01, 0x08, 0x06, 0x01, 0x18, 0x04, 0x01, 0x30, 0x8C, 0x00, 0x70, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S
+ 0x0C, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
+ 0x0C, 0xF8, 0x0F, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xE0, 0x00, 0xF8, 0x7F, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U
+ 0x0D, 0x08, 0x00, 0x00, 0x78, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x01, 0x00, 0x3E, 0x00, 0x80, 0x0F, 0x00, 0xF0, 0x01, 0x00, 0x78, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V
+ 0x13, 0x78, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xC0, 0x01, 0x00, 0xE0, 0x01, 0x00, 0x7C, 0x00, 0xC0, 0x0F, 0x00, 0xF8, 0x01, 0x00, 0x18, 0x00, 0x00, 0xF8, 0x01, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xE0, 0x01, 0x00, 0xC0, 0x01, 0x00, 0xF8, 0x01, 0x80, 0x3F, 0x00, 0xF0, 0x03, 0x00, 0x78, 0x00, 0x00, // Code for char W
+ 0x0C, 0x08, 0x00, 0x01, 0x18, 0xC0, 0x01, 0x30, 0xE0, 0x00, 0xE0, 0x30, 0x00, 0xC0, 0x1D, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x00, 0xC0, 0x1D, 0x00, 0xE0, 0x38, 0x00, 0x70, 0xE0, 0x00, 0x18, 0xC0, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X
+ 0x0C, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x80, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0xFE, 0x01, 0x80, 0x03, 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0x00, 0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y
+ 0x0B, 0x08, 0x80, 0x01, 0x08, 0xC0, 0x01, 0x08, 0x70, 0x01, 0x08, 0x38, 0x01, 0x08, 0x0C, 0x01, 0x08, 0x07, 0x01, 0x88, 0x03, 0x01, 0xC8, 0x00, 0x01, 0x78, 0x00, 0x01, 0x38, 0x00, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z
+ 0x04, 0xFC, 0xFF, 0x1F, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
+ 0x06, 0x0C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash
+ 0x04, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0xFC, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
+ 0x08, 0x00, 0x04, 0x00, 0x80, 0x07, 0x00, 0xF0, 0x01, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x80, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^
+ 0x0C, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _
+ 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xC0, 0x0C, 0x01, 0x40, 0x04, 0x01, 0x40, 0x04, 0x01, 0x40, 0x04, 0x01, 0x40, 0x84, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a
+ 0x09, 0xFC, 0xFF, 0x01, 0xFC, 0xFF, 0x01, 0x80, 0xC1, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0xC0, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b
+ 0x09, 0x00, 0x3E, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x80, 0xC1, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c
+ 0x09, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x80, 0xC1, 0x00, 0xFC, 0xFF, 0x01, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d
+ 0x0A, 0x00, 0x3F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x88, 0x00, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0xC0, 0x88, 0x01, 0x80, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e
+ 0x06, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xFC, 0xFF, 0x01, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
+ 0x09, 0x00, 0x7F, 0x04, 0x80, 0xFF, 0x0C, 0xC0, 0x80, 0x19, 0x40, 0x00, 0x11, 0x40, 0x00, 0x11, 0x40, 0x80, 0x10, 0x80, 0x41, 0x18, 0xC0, 0xFF, 0x0F, 0xC0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g
+ 0x09, 0xFC, 0xFF, 0x01, 0xFC, 0xFF, 0x01, 0x80, 0x01, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h
+ 0x02, 0xCC, 0xFF, 0x01, 0xCC, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
+ 0x03, 0x00, 0x00, 0x10, 0xCC, 0xFF, 0x1F, 0xCC, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
+ 0x09, 0xFC, 0xFF, 0x01, 0xFC, 0xFF, 0x01, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x3E, 0x00, 0x80, 0x73, 0x00, 0xC0, 0xE1, 0x00, 0x40, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k
+ 0x02, 0xFC, 0xFF, 0x01, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
+ 0x0E, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m
+ 0x09, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x80, 0x01, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n
+ 0x0A, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0xC0, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o
+ 0x09, 0xC0, 0xFF, 0x1F, 0xC0, 0xFF, 0x1F, 0x80, 0xC1, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0xC0, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p
+ 0x09, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x80, 0xC1, 0x00, 0xC0, 0xFF, 0x1F, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q
+ 0x06, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r
+ 0x09, 0x80, 0xC3, 0x00, 0x80, 0x87, 0x00, 0x40, 0x0C, 0x01, 0x40, 0x0C, 0x01, 0x40, 0x0C, 0x01, 0x40, 0x08, 0x01, 0xC0, 0x18, 0x01, 0x80, 0xF9, 0x00, 0x80, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
+ 0x06, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t
+ 0x09, 0xC0, 0x7F, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xC0, 0x00, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u
+ 0x0B, 0x40, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x01, 0x00, 0x7C, 0x00, 0x80, 0x0F, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v
+ 0x0F, 0xC0, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xE0, 0x01, 0x00, 0xC0, 0x01, 0x00, 0xFC, 0x00, 0xC0, 0x0F, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xC0, 0x01, 0x00, 0xE0, 0x01, 0x00, 0x7E, 0x00, 0xC0, 0x07, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char w
+ 0x08, 0xC0, 0x80, 0x01, 0xC0, 0xC1, 0x01, 0x00, 0x77, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x77, 0x00, 0xC0, 0xC1, 0x01, 0xC0, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x
+ 0x0B, 0x40, 0x00, 0x00, 0xC0, 0x01, 0x10, 0x80, 0x0F, 0x10, 0x00, 0x3C, 0x18, 0x00, 0xF0, 0x0E, 0x00, 0x80, 0x07, 0x00, 0xE0, 0x01, 0x00, 0x3C, 0x00, 0x80, 0x0F, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
+ 0x07, 0x40, 0x80, 0x01, 0x40, 0xE0, 0x01, 0x40, 0x78, 0x01, 0x40, 0x1E, 0x01, 0xC0, 0x07, 0x01, 0xC0, 0x03, 0x01, 0xC0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z
+ 0x07, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x36, 0x00, 0xF8, 0xF7, 0x0F, 0x0C, 0x00, 0x18, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char {
+ 0x02, 0xFC, 0xFF, 0x1F, 0xFC, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
+ 0x07, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x0C, 0x00, 0x18, 0xF8, 0xF7, 0x0F, 0x00, 0x36, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
+ 0x0A, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~
+ 0x05, 0xFC, 0xFF, 0x00, 0x04, 0x80, 0x00, 0x04, 0x80, 0x00, 0x04, 0x80, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x7F (DEL)
+
+ // Custom characters inserted at 0x80 to 0x86
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for thin space
+ 0x13, 0x00, 0x0C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x3F, 0x00, 0x80, 0x7F, 0x00, 0xC0, 0xED, 0x00, 0x40, 0x8C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, // Code for left arrow 
+ 0x0A, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1C, 0x00, 0x00, 0xFE, 0xFF, 0x0F, 0xFE, 0xFF, 0x0F, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for up arrow
+ 0x13, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x40, 0x8C, 0x00, 0xC0, 0xED, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0C, 0x00, // Code for right arrow
+ 0x0A, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x03, 0x00, 0x00, 0x07, 0xFE, 0xFF, 0x0F, 0xFE, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, 0x80, 0x03, 0x00, 0x80, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for down arrow
+ 0x0A, 0xC0, 0x60, 0x00, 0x60, 0xC0, 0x00, 0x30, 0x80, 0x01, 0x18, 0x00, 0x03, 0xFC, 0xFF, 0x07, 0xFC, 0xFF, 0x07, 0x18, 0x00, 0x03, 0x30, 0x80, 0x01, 0x60, 0xC0, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for more arrow
+ 0x0A, 0x18, 0x00, 0x03, 0x30, 0x80, 0x01, 0x60, 0xC0, 0x00, 0xC0, 0x60, 0x00, 0xFE, 0xF1, 0x0F, 0xFE, 0xF1, 0x0F, 0xC0, 0x60, 0x00, 0x60, 0xC0, 0x00, 0x30, 0x80, 0x01, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for less arrow
+
+ // Non-latin characters 0x87 to 0x17F
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x87
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x88
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x89
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x8A
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x8B
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x8C
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x8D
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x8E
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x8F
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x90
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0xA0
+ 0x02, 0xC0, 0xFC, 0x0F, 0xC0, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0x00, 0x06, 0x00, 0xC0, 0x3F, 0x00, 0xC0, 0x30, 0x00, 0x20, 0x60, 0x00, 0x20, 0x40, 0x00, 0x38, 0xC0, 0x01, 0x20, 0x40, 0x00, 0x60, 0x60, 0x00, 0xC0, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0x82, 0x01, 0x00, 0xE2, 0x01, 0xF0, 0x7F, 0x01, 0x38, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x18, 0x02, 0x01, 0x30, 0x80, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x04, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x31, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x31, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0x08, 0x00, 0x00, 0x18, 0x24, 0x00, 0x70, 0x24, 0x00, 0xE0, 0x25, 0x00, 0x80, 0x27, 0x00, 0x00, 0xFE, 0x01, 0x00, 0xFE, 0x01, 0x80, 0x27, 0x00, 0xE0, 0x24, 0x00, 0x70, 0x24, 0x00, 0x18, 0x24, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x02, 0xFC, 0xE3, 0x1F, 0xFC, 0xE3, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x00, 0x01, 0x78, 0x0F, 0x03, 0x7C, 0x19, 0x06, 0xC4, 0x10, 0x04, 0x84, 0x10, 0x04, 0x84, 0x10, 0x04, 0x84, 0x31, 0x04, 0x8C, 0x31, 0x06, 0x18, 0xDF, 0x03, 0x10, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x06, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0F, 0x00, 0x0F, 0x00, 0xC0, 0x30, 0x00, 0x20, 0x40, 0x00, 0x10, 0x80, 0x00, 0x90, 0x9F, 0x00, 0xC8, 0x30, 0x01, 0x48, 0x20, 0x01, 0x48, 0x20, 0x01, 0x48, 0x20, 0x01, 0xC8, 0x30, 0x01, 0x90, 0x90, 0x00, 0x10, 0x80, 0x00, 0x20, 0x40, 0x00, 0xC0, 0x30, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x07, 0x80, 0x03, 0x00, 0xD8, 0x07, 0x00, 0x48, 0x04, 0x00, 0x48, 0x04, 0x00, 0x58, 0x02, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x1C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x63, 0x00, 0x80, 0xC1, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x63, 0x00, 0x80, 0xC1, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x05, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0F, 0x00, 0x0F, 0x00, 0xC0, 0x30, 0x00, 0x20, 0x40, 0x00, 0x10, 0x80, 0x00, 0x10, 0x80, 0x00, 0xC8, 0x3F, 0x01, 0x48, 0x04, 0x01, 0x48, 0x04, 0x01, 0x48, 0x1C, 0x01, 0xC8, 0x37, 0x01, 0x10, 0xA1, 0x00, 0x10, 0x80, 0x00, 0x20, 0x40, 0x00, 0xC0, 0x30, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x06, 0x60, 0x00, 0x00, 0x98, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x98, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0xB0 (degree symbol)
+ 0x0A, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0xE0, 0x3F, 0x01, 0xE0, 0x3F, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x06, 0x00, 0x08, 0x00, 0x30, 0x0C, 0x00, 0x08, 0x0A, 0x00, 0x08, 0x09, 0x00, 0x88, 0x09, 0x00, 0xF0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x05, 0x10, 0x0C, 0x00, 0x08, 0x08, 0x00, 0x88, 0x08, 0x00, 0xC8, 0x08, 0x00, 0x70, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x04, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0xC0, 0xFF, 0x1F, 0xC0, 0xFF, 0x1F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x01, 0xC0, 0x7F, 0x00, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0xF0, 0x01, 0x00, 0xF8, 0x03, 0x00, 0xF8, 0x03, 0x00, 0xF8, 0xFF, 0x0F, 0xF8, 0xFF, 0x0F, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0xFF, 0x0F, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x02, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x14, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x06, 0x10, 0x08, 0x00, 0x18, 0x08, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x08, 0xE0, 0x01, 0x00, 0xF0, 0x03, 0x00, 0x18, 0x06, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0xF0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x80, 0x80, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x63, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x80, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x63, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x10, 0x10, 0x08, 0x00, 0x18, 0x08, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x08, 0x01, 0x00, 0xC8, 0x01, 0x00, 0x60, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x07, 0x00, 0x80, 0x61, 0x00, 0xE0, 0x70, 0x00, 0x30, 0x4C, 0x00, 0x18, 0x46, 0x00, 0x00, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x10, 0x10, 0x08, 0x00, 0x18, 0x08, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x08, 0x01, 0x00, 0xC8, 0x01, 0x00, 0x60, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x07, 0x00, 0x80, 0x01, 0x01, 0xE0, 0x86, 0x01, 0x30, 0x41, 0x01, 0x18, 0x21, 0x01, 0x00, 0x31, 0x01, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x10, 0x10, 0x0C, 0x00, 0x08, 0x08, 0x00, 0x88, 0x08, 0x00, 0xC8, 0x08, 0x01, 0x70, 0xC7, 0x01, 0x00, 0x60, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x07, 0x00, 0x80, 0x61, 0x00, 0xE0, 0x70, 0x00, 0x30, 0x4C, 0x00, 0x18, 0x46, 0x00, 0x00, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x80, 0x01, 0x00, 0xC0, 0x07, 0x00, 0x60, 0x04, 0x00, 0x30, 0x08, 0x00, 0x18, 0x08, 0xC0, 0x1C, 0x08, 0xC0, 0x04, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0xE0, 0x0B, 0x00, 0x79, 0x08, 0x00, 0x19, 0x08, 0x00, 0x7A, 0x08, 0x00, 0xE0, 0x0B, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0xE0, 0x0B, 0x00, 0x78, 0x08, 0x00, 0x1A, 0x08, 0x00, 0x79, 0x08, 0x00, 0xE1, 0x0B, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0xE2, 0x0B, 0x00, 0x7B, 0x08, 0x00, 0x19, 0x08, 0x00, 0x7B, 0x08, 0x00, 0xE2, 0x0B, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x03, 0x0F, 0x00, 0xE1, 0x0B, 0x00, 0x79, 0x08, 0x00, 0x1B, 0x08, 0x00, 0x7A, 0x08, 0x00, 0xE2, 0x0B, 0x00, 0x03, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x03, 0x0F, 0x00, 0xE3, 0x0B, 0x00, 0x78, 0x08, 0x00, 0x18, 0x08, 0x00, 0x78, 0x08, 0x00, 0xE3, 0x0B, 0x00, 0x03, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0xF8, 0x00, 0x00, 0x0F, 0x00, 0xE0, 0x0B, 0x00, 0x7E, 0x08, 0x00, 0x19, 0x08, 0x00, 0x79, 0x08, 0x00, 0xEE, 0x0B, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x13, 0x00, 0x80, 0x01, 0x00, 0xE0, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x0B, 0x00, 0xE0, 0x08, 0x00, 0x38, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, // Code for char �
+ 0x0D, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0xC0, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x07, 0x08, 0x00, 0x15, 0x08, 0x00, 0x19, 0x18, 0x80, 0x01, 0x30, 0x80, 0x00, 0x70, 0xE0, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x09, 0x02, 0x01, 0x09, 0x02, 0x01, 0x0A, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x0A, 0x02, 0x01, 0x09, 0x02, 0x01, 0x09, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x0A, 0x02, 0x01, 0x0B, 0x02, 0x01, 0x09, 0x02, 0x01, 0x0B, 0x02, 0x01, 0x0A, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x0B, 0x02, 0x01, 0x0B, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x0B, 0x02, 0x01, 0x0B, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x03, 0x01, 0x00, 0x00, 0xF9, 0xFF, 0x01, 0xFA, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x03, 0xFA, 0xFF, 0x01, 0xF9, 0xFF, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x05, 0x02, 0x00, 0x00, 0xFB, 0xFF, 0x01, 0xF9, 0xFF, 0x01, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x07, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x02, 0x01, 0x08, 0x00, 0x01, 0x18, 0x80, 0x01, 0x10, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0xF8, 0xFF, 0x01, 0x38, 0x00, 0x00, 0x73, 0x00, 0x00, 0xC1, 0x01, 0x00, 0x01, 0x07, 0x00, 0x03, 0x1E, 0x00, 0x02, 0x38, 0x00, 0x02, 0xE0, 0x00, 0x03, 0xC0, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x09, 0x00, 0x01, 0x09, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x18, 0x80, 0x01, 0x10, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x18, 0x80, 0x01, 0x08, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x09, 0x00, 0x01, 0x09, 0x00, 0x01, 0x18, 0x80, 0x01, 0x10, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x18, 0x80, 0x01, 0x0A, 0x00, 0x01, 0x0B, 0x00, 0x01, 0x09, 0x00, 0x01, 0x0B, 0x00, 0x01, 0x1A, 0x80, 0x01, 0x10, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x1B, 0x80, 0x01, 0x09, 0x00, 0x01, 0x09, 0x00, 0x01, 0x0B, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x1A, 0x80, 0x01, 0x13, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x80, 0x1F, 0x00, 0xE0, 0x7F, 0x00, 0x70, 0xE0, 0x00, 0x10, 0x80, 0x00, 0x1B, 0x80, 0x01, 0x0B, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, 0x1B, 0x80, 0x01, 0x13, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x08, 0x60, 0x30, 0x00, 0xC0, 0x18, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x80, 0x0D, 0x00, 0xC0, 0x18, 0x00, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0E, 0x80, 0x1F, 0x01, 0xE0, 0xFF, 0x01, 0x70, 0xE0, 0x00, 0x10, 0xE0, 0x00, 0x18, 0xB0, 0x01, 0x08, 0x18, 0x01, 0x08, 0x0E, 0x01, 0x08, 0x07, 0x01, 0x88, 0x01, 0x01, 0xD8, 0x80, 0x01, 0x70, 0x80, 0x00, 0x70, 0xE0, 0x00, 0xF8, 0x7F, 0x00, 0x88, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0xF8, 0x0F, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xE0, 0x00, 0xF8, 0x7F, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0xF8, 0x0F, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xE0, 0x00, 0xF8, 0x7F, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0xF8, 0x0F, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x02, 0x00, 0x01, 0x03, 0x00, 0x01, 0x01, 0x00, 0x01, 0x03, 0x00, 0x01, 0x02, 0x80, 0x01, 0x00, 0xE0, 0x00, 0xF8, 0x7F, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0xF8, 0x0F, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x80, 0x01, 0x03, 0xE0, 0x00, 0xF8, 0x7F, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0C, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x80, 0x03, 0x00, 0x02, 0xFE, 0x01, 0x01, 0xFE, 0x01, 0x81, 0x03, 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0x00, 0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x20, 0x20, 0x00, 0x20, 0x20, 0x00, 0x20, 0x20, 0x00, 0x20, 0x20, 0x00, 0x20, 0x20, 0x00, 0x20, 0x20, 0x00, 0x60, 0x18, 0x00, 0xC0, 0x1F, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0xC0, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x18, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x80, 0x01, 0x04, 0x00, 0x01, 0x84, 0x07, 0x01, 0xCC, 0x0E, 0x01, 0x78, 0x0C, 0x01, 0x30, 0xF8, 0x01, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xC4, 0x0C, 0x01, 0x44, 0x04, 0x01, 0x4C, 0x04, 0x01, 0x50, 0x04, 0x01, 0x40, 0x84, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xC0, 0x0C, 0x01, 0x40, 0x04, 0x01, 0x50, 0x04, 0x01, 0x48, 0x04, 0x01, 0x4C, 0x84, 0x00, 0xC4, 0x7F, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xD0, 0x0C, 0x01, 0x58, 0x04, 0x01, 0x4C, 0x04, 0x01, 0x44, 0x04, 0x01, 0x4C, 0x84, 0x00, 0xD8, 0x7F, 0x00, 0x90, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xD8, 0x0C, 0x01, 0x48, 0x04, 0x01, 0x48, 0x04, 0x01, 0x58, 0x04, 0x01, 0x50, 0x84, 0x00, 0xD0, 0x7F, 0x00, 0x88, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xCC, 0x0C, 0x01, 0x4C, 0x04, 0x01, 0x40, 0x04, 0x01, 0x40, 0x04, 0x01, 0x4C, 0x84, 0x00, 0xCC, 0x7F, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x00, 0xF1, 0x00, 0x80, 0xF9, 0x01, 0xC0, 0x0C, 0x01, 0x4E, 0x04, 0x01, 0x51, 0x04, 0x01, 0x51, 0x04, 0x01, 0x4E, 0x84, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x11, 0x00, 0xF9, 0x00, 0x80, 0xF9, 0x01, 0xC0, 0x0C, 0x01, 0x40, 0x04, 0x01, 0x40, 0x04, 0x01, 0x40, 0x84, 0x01, 0x40, 0xC4, 0x00, 0x80, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xC9, 0x00, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0xC0, 0x88, 0x01, 0x80, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0x00, 0x3E, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x07, 0x40, 0x00, 0x15, 0x40, 0x00, 0x19, 0x80, 0xC1, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x3F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x88, 0x00, 0x44, 0x08, 0x01, 0x44, 0x08, 0x01, 0x4C, 0x08, 0x01, 0x50, 0x08, 0x01, 0xC0, 0x88, 0x01, 0x80, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x3F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x88, 0x00, 0x40, 0x08, 0x01, 0x50, 0x08, 0x01, 0x48, 0x08, 0x01, 0x4C, 0x08, 0x01, 0xC4, 0x88, 0x01, 0x80, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x3F, 0x00, 0x80, 0xFF, 0x00, 0xD0, 0x88, 0x00, 0x58, 0x08, 0x01, 0x4C, 0x08, 0x01, 0x44, 0x08, 0x01, 0x4C, 0x08, 0x01, 0xD8, 0x88, 0x01, 0x90, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x3F, 0x00, 0x80, 0xFF, 0x00, 0xCC, 0x88, 0x00, 0x4C, 0x08, 0x01, 0x40, 0x08, 0x01, 0x40, 0x08, 0x01, 0x4C, 0x08, 0x01, 0xCC, 0x88, 0x01, 0x80, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x04, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xC8, 0xFF, 0x01, 0xD0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x04, 0xD0, 0xFF, 0x01, 0xC8, 0xFF, 0x01, 0x0C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x07, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0xCC, 0xFF, 0x01, 0xC4, 0xFF, 0x01, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x06, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0xA4, 0x81, 0x01, 0x94, 0x00, 0x01, 0x98, 0x00, 0x01, 0x98, 0x00, 0x01, 0xB8, 0x00, 0x01, 0xEC, 0x81, 0x01, 0xC4, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0xC0, 0xFF, 0x01, 0xD8, 0xFF, 0x01, 0x88, 0x01, 0x00, 0x48, 0x00, 0x00, 0x58, 0x00, 0x00, 0x50, 0x00, 0x00, 0xD0, 0x00, 0x00, 0xC8, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x44, 0x00, 0x01, 0x4C, 0x00, 0x01, 0x48, 0x00, 0x01, 0x50, 0x00, 0x01, 0xC0, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xC0, 0x80, 0x01, 0x40, 0x00, 0x01, 0x50, 0x00, 0x01, 0x48, 0x00, 0x01, 0x4C, 0x00, 0x01, 0xC4, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xD0, 0x80, 0x01, 0x58, 0x00, 0x01, 0x4C, 0x00, 0x01, 0x44, 0x00, 0x01, 0x4C, 0x00, 0x01, 0xD8, 0x80, 0x01, 0x90, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xD8, 0x80, 0x01, 0x48, 0x00, 0x01, 0x48, 0x00, 0x01, 0x58, 0x00, 0x01, 0x50, 0x00, 0x01, 0xD0, 0x80, 0x01, 0x88, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x7F, 0x00, 0x80, 0xFF, 0x00, 0xCC, 0x80, 0x01, 0x4C, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x4C, 0x00, 0x01, 0xCC, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x60, 0x32, 0x00, 0x60, 0x32, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0x00, 0x1C, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xC1, 0x00, 0x40, 0x70, 0x01, 0x40, 0x18, 0x01, 0x40, 0x0C, 0x01, 0x40, 0x07, 0x01, 0x80, 0xC1, 0x00, 0xC0, 0xFF, 0x00, 0x40, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0xC0, 0x7F, 0x00, 0xC0, 0xFF, 0x00, 0x04, 0x80, 0x01, 0x0C, 0x00, 0x01, 0x08, 0x00, 0x01, 0x10, 0x00, 0x01, 0x00, 0xC0, 0x00, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0xC0, 0x7F, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x80, 0x01, 0x10, 0x00, 0x01, 0x08, 0x00, 0x01, 0x0C, 0x00, 0x01, 0x04, 0xC0, 0x00, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0xC0, 0x7F, 0x00, 0xD0, 0xFF, 0x00, 0x18, 0x80, 0x01, 0x0C, 0x00, 0x01, 0x04, 0x00, 0x01, 0x0C, 0x00, 0x01, 0x18, 0xC0, 0x00, 0xD0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x09, 0xC0, 0x7F, 0x00, 0xCC, 0xFF, 0x00, 0x0C, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x01, 0x0C, 0xC0, 0x00, 0xC0, 0xFF, 0x01, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x40, 0x00, 0x00, 0xC0, 0x01, 0x10, 0x80, 0x0F, 0x10, 0x00, 0x3C, 0x18, 0x10, 0xF0, 0x0E, 0x08, 0x80, 0x07, 0x0C, 0xE0, 0x01, 0x04, 0x3C, 0x00, 0x80, 0x0F, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0A, 0xFC, 0xFF, 0x1F, 0xFC, 0xFF, 0x1F, 0x00, 0xC1, 0x00, 0x80, 0x80, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0xC0, 0x80, 0x01, 0x80, 0xFF, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0B, 0x40, 0x00, 0x00, 0xC0, 0x01, 0x10, 0x8C, 0x0F, 0x10, 0x0C, 0x3C, 0x18, 0x00, 0xF0, 0x0E, 0x00, 0x80, 0x07, 0x0C, 0xE0, 0x01, 0x0C, 0x3C, 0x00, 0x80, 0x0F, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xC0, 0x01, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0xC4, 0x17, 0x00, 0xF4, 0x10, 0x00, 0x14, 0x10, 0x00, 0xF4, 0x10, 0x00, 0xC4, 0x17, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x20, 0xBB, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0xC9, 0x00, 0x00, 0xFF, 0x01, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xC0, 0x01, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0xC3, 0x17, 0x00, 0xF4, 0x10, 0x00, 0x14, 0x10, 0x00, 0xF4, 0x10, 0x00, 0xC3, 0x17, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0xBB, 0x01, 0x98, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0xC9, 0x00, 0x18, 0xFF, 0x01, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x01, 0x00, 0xC0, 0x01, 0x00, 0x78, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x17, 0x00, 0xF0, 0x10, 0x00, 0x10, 0x10, 0x00, 0xF0, 0x10, 0x00, 0xC0, 0x17, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x78, 0x1E, 0x00, 0xE0, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0xBB, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0xC9, 0x00, 0x00, 0xFF, 0x1F, 0x00, 0xFE, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x14, 0x00, 0x01, 0x13, 0x00, 0x01, 0x13, 0x00, 0x01, 0x21, 0x80, 0x00, 0xE0, 0xC0, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0xA0, 0x00, 0x01, 0x98, 0x00, 0x01, 0x88, 0x81, 0x01, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x14, 0x00, 0x01, 0x16, 0x00, 0x01, 0x13, 0x00, 0x01, 0x13, 0x00, 0x01, 0x16, 0x00, 0x01, 0x24, 0x80, 0x00, 0xE0, 0xC0, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0xA0, 0x81, 0x01, 0x90, 0x00, 0x01, 0x98, 0x00, 0x01, 0x98, 0x00, 0x01, 0xB0, 0x81, 0x01, 0x20, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x16, 0x00, 0x01, 0x16, 0x00, 0x01, 0x10, 0x00, 0x01, 0x20, 0x80, 0x00, 0xE0, 0xC0, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x11, 0x00, 0x01, 0x13, 0x00, 0x01, 0x16, 0x00, 0x01, 0x16, 0x00, 0x01, 0x13, 0x00, 0x01, 0x21, 0x80, 0x00, 0xE0, 0xC0, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x88, 0x81, 0x01, 0x98, 0x00, 0x01, 0xB0, 0x00, 0x01, 0xB0, 0x00, 0x01, 0x98, 0x81, 0x01, 0x08, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x11, 0x00, 0x01, 0x13, 0x00, 0x01, 0x16, 0x00, 0x01, 0x16, 0x00, 0x01, 0x13, 0x00, 0x01, 0x21, 0x80, 0x00, 0x60, 0xC0, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x81, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x20, 0x80, 0x00, 0x60, 0xC0, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0x20, 0x81, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x14, 0x04, 0x01, 0x14, 0x04, 0x01, 0x14, 0x04, 0x01, 0x14, 0x04, 0x01, 0x14, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0xA0, 0x89, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x89, 0x01, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x13, 0x04, 0x01, 0x14, 0x04, 0x01, 0x14, 0x04, 0x01, 0x14, 0x04, 0x01, 0x13, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x98, 0x89, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0xA0, 0x08, 0x01, 0x98, 0x89, 0x01, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x16, 0x04, 0x01, 0x16, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x89, 0x01, 0x80, 0x08, 0x01, 0xB0, 0x08, 0x01, 0xB0, 0x08, 0x01, 0x80, 0x89, 0x01, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x1F, 0x10, 0x04, 0x13, 0x10, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x89, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x1F, 0x80, 0x08, 0x13, 0x80, 0x89, 0x11, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x11, 0x04, 0x01, 0x13, 0x04, 0x01, 0x16, 0x04, 0x01, 0x16, 0x04, 0x01, 0x13, 0x04, 0x01, 0x11, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x88, 0x89, 0x01, 0x98, 0x08, 0x01, 0xB0, 0x08, 0x01, 0xB0, 0x08, 0x01, 0x98, 0x89, 0x01, 0x08, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x14, 0x00, 0x01, 0x16, 0x00, 0x01, 0x13, 0x00, 0x01, 0x13, 0x04, 0x01, 0x16, 0x04, 0x01, 0x34, 0x84, 0x01, 0xE0, 0x84, 0x00, 0xC0, 0xFC, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x04, 0x00, 0xFF, 0x0C, 0xA0, 0x81, 0x19, 0x90, 0x00, 0x11, 0x98, 0x00, 0x11, 0x98, 0x00, 0x11, 0x30, 0xC3, 0x18, 0xA0, 0xFF, 0x0F, 0x80, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x13, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x04, 0x01, 0x13, 0x04, 0x01, 0x30, 0x84, 0x01, 0xE0, 0x84, 0x00, 0xC0, 0xFC, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x04, 0x00, 0xFF, 0x0C, 0x98, 0x81, 0x19, 0xA0, 0x00, 0x11, 0xA0, 0x00, 0x11, 0xA0, 0x00, 0x11, 0x18, 0xC3, 0x18, 0x80, 0xFF, 0x0F, 0x80, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x16, 0x00, 0x01, 0x16, 0x04, 0x01, 0x10, 0x04, 0x01, 0x30, 0x84, 0x01, 0xE0, 0x84, 0x00, 0xC0, 0xFC, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x04, 0x00, 0xFF, 0x0C, 0x80, 0x81, 0x19, 0xB0, 0x00, 0x11, 0xB0, 0x00, 0x11, 0x80, 0x00, 0x11, 0x00, 0xC3, 0x18, 0x80, 0xFF, 0x0F, 0x80, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x15, 0x10, 0x04, 0x0D, 0x10, 0x04, 0x01, 0x30, 0x84, 0x01, 0xE0, 0x84, 0x00, 0xC0, 0xFC, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x04, 0x00, 0xFF, 0x0C, 0x80, 0x81, 0x19, 0xB8, 0x00, 0x11, 0xB4, 0x00, 0x11, 0x80, 0x00, 0x11, 0x00, 0xC3, 0x18, 0x80, 0xFF, 0x0F, 0x80, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char #
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x00, 0x04, 0x02, 0x00, 0x06, 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x02, 0x00, 0x06, 0x02, 0x00, 0x04, 0x02, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $
+ 0x09, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xFA, 0xFF, 0x01, 0x03, 0x01, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char %
+ 0x0D, 0x40, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char &
+ 0x09, 0x10, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x10, 0x01, 0x00, 0x90, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
+ 0x06, 0x06, 0x00, 0x00, 0x02, 0x00, 0x00, 0xF2, 0xFF, 0x01, 0xF4, 0xFF, 0x01, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
+ 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x90, 0xFF, 0x01, 0xB0, 0xFF, 0x01, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char )
+ 0x05, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0xF4, 0xFF, 0x01, 0xF4, 0xFF, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char *
+ 0x05, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0xA0, 0xFF, 0x01, 0xA0, 0xFF, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char +
+ 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF4, 0xFF, 0x01, 0xF4, 0xFF, 0x01, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
+ 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xA0, 0xFF, 0x01, 0xA0, 0xFF, 0x01, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char -
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x1F, 0xF0, 0xFF, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
+ 0x04, 0x00, 0x00, 0x00, 0x98, 0xFF, 0x1F, 0x98, 0xFF, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char /
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xFF, 0x01, 0xF6, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x20, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x80, 0x01, 0xF0, 0xFF, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2
+ 0x07, 0x00, 0x00, 0x00, 0x98, 0xFF, 0x01, 0x98, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x98, 0xFF, 0x1F, 0x98, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3
+ 0x09, 0x00, 0x20, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x04, 0x00, 0x01, 0x16, 0x00, 0x01, 0x13, 0x00, 0x01, 0x13, 0x80, 0x01, 0xF6, 0xFF, 0x00, 0xF4, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4
+ 0x05, 0x30, 0x00, 0x10, 0x98, 0xFF, 0x1F, 0x98, 0xFF, 0x0F, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x80, 0x0D, 0x14, 0xC0, 0x18, 0x0C, 0x60, 0x70, 0x00, 0x30, 0xE0, 0x00, 0x10, 0xC0, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6
+ 0x09, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x18, 0x00, 0x00, 0x1C, 0x14, 0x00, 0x76, 0x0C, 0x80, 0xE3, 0x00, 0x80, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7
+ 0x0A, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x18, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x63, 0x00, 0x80, 0xC1, 0x00, 0x80, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x04, 0x00, 0x01, 0x03, 0x00, 0x01, 0x03, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9
+ 0x04, 0x00, 0x00, 0x00, 0xFA, 0xFF, 0x01, 0xF9, 0xFF, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
+ 0x03, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x15, 0xF8, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char <
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x01, 0x70, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char =
+ 0x06, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x06, 0x01, 0x00, 0x06, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ?
+ 0x06, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char @
+ 0x0A, 0x00, 0x18, 0x00, 0x00, 0x08, 0x00, 0xF0, 0xFF, 0x01, 0x00, 0x06, 0x01, 0x00, 0x02, 0x01, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A
+ 0x04, 0x00, 0x18, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x70, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x80, 0x03, 0x00, 0x04, 0x0E, 0x00, 0x03, 0x38, 0x00, 0x03, 0x70, 0x00, 0x01, 0xC0, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x98, 0x00, 0x00, 0x88, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x70, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x80, 0x03, 0x00, 0x00, 0x0E, 0x14, 0x00, 0x38, 0x0C, 0x00, 0x70, 0x00, 0x00, 0xC0, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x14, 0x80, 0x00, 0x0C, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x70, 0x00, 0x00, 0xC1, 0x01, 0x00, 0x83, 0x03, 0x00, 0x06, 0x0E, 0x00, 0x06, 0x38, 0x00, 0x03, 0x70, 0x00, 0x01, 0xC0, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x88, 0xFF, 0x01, 0x18, 0x01, 0x00, 0xB0, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x98, 0x00, 0x00, 0x88, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H
+ 0x0A, 0xB0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xC0, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x80, 0x00, 0x10, 0x80, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x70, 0x80, 0x01, 0xE0, 0xFF, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J
+ 0x0A, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x80, 0x00, 0x10, 0x80, 0xFF, 0x1F, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x20, 0x80, 0x00, 0x60, 0xC0, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0xA0, 0x81, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x13, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x14, 0x00, 0x01, 0x13, 0x00, 0x01, 0x20, 0x80, 0x00, 0x60, 0xC0, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x98, 0x81, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0x98, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x20, 0x80, 0x00, 0x14, 0x00, 0x01, 0x16, 0x00, 0x01, 0x13, 0x00, 0x01, 0x11, 0x00, 0x01, 0x14, 0x00, 0x01, 0x23, 0x80, 0x00, 0x61, 0xC0, 0x00, 0xC1, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0xA0, 0x81, 0x01, 0xB0, 0x00, 0x01, 0x98, 0x00, 0x01, 0xA8, 0x00, 0x01, 0xB0, 0x81, 0x01, 0x18, 0xFF, 0x00, 0x08, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q
+ 0x12, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xC0, 0x00, 0x30, 0x80, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, // Code for char R
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x89, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x89, 0x01, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x14, 0x04, 0x00, 0x13, 0x04, 0x00, 0x13, 0x1C, 0x00, 0x11, 0x3C, 0x00, 0x30, 0xE2, 0x00, 0xE0, 0xC3, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
+ 0x06, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x20, 0x01, 0x00, 0x98, 0x01, 0x00, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x14, 0x10, 0x04, 0x0C, 0x10, 0x1C, 0x00, 0x10, 0x3C, 0x00, 0x30, 0xE2, 0x00, 0xE0, 0xC3, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V
+ 0x06, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x15, 0x80, 0xFF, 0x0D, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char W
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x00, 0x11, 0x04, 0x00, 0x13, 0x04, 0x00, 0x16, 0x04, 0x00, 0x16, 0x04, 0x00, 0x13, 0x1C, 0x00, 0x11, 0x3C, 0x00, 0x30, 0xE2, 0x00, 0xE0, 0xC3, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X
+ 0x07, 0x00, 0x00, 0x00, 0x88, 0xFF, 0x01, 0x98, 0xFF, 0x01, 0x30, 0x01, 0x00, 0xB0, 0x01, 0x00, 0x98, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xE0, 0xE1, 0x00, 0x20, 0x83, 0x01, 0x10, 0x02, 0x01, 0x10, 0x06, 0x01, 0x14, 0x04, 0x01, 0x13, 0x04, 0x01, 0x11, 0x04, 0x01, 0x30, 0x8C, 0x01, 0x60, 0xF8, 0x00, 0x40, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x80, 0x8F, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0xA0, 0x18, 0x01, 0x98, 0x18, 0x01, 0x88, 0xB1, 0x01, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xE0, 0xE1, 0x00, 0x20, 0x83, 0x01, 0x14, 0x02, 0x01, 0x16, 0x06, 0x01, 0x13, 0x04, 0x01, 0x13, 0x04, 0x01, 0x16, 0x04, 0x01, 0x34, 0x8C, 0x01, 0x60, 0xF8, 0x00, 0x40, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0xA0, 0x8F, 0x01, 0x90, 0x08, 0x01, 0x98, 0x08, 0x01, 0x98, 0x18, 0x01, 0xB0, 0x18, 0x01, 0xA0, 0xB1, 0x01, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xE0, 0xE1, 0x00, 0x20, 0x83, 0x01, 0x10, 0x02, 0x01, 0x10, 0x06, 0x11, 0x10, 0x04, 0x17, 0x10, 0x04, 0x1D, 0x10, 0x04, 0x01, 0x30, 0x8C, 0x01, 0x60, 0xF8, 0x00, 0x40, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x80, 0x8F, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x11, 0x80, 0x18, 0x17, 0x80, 0x18, 0x1D, 0x80, 0xB1, 0x01, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xE0, 0xE1, 0x00, 0x20, 0x83, 0x01, 0x11, 0x02, 0x01, 0x13, 0x06, 0x01, 0x16, 0x04, 0x01, 0x16, 0x04, 0x01, 0x13, 0x04, 0x01, 0x31, 0x8C, 0x01, 0x60, 0xF8, 0x00, 0x40, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x88, 0x8F, 0x01, 0x98, 0x08, 0x01, 0xB0, 0x08, 0x01, 0xB0, 0x18, 0x01, 0x98, 0x18, 0x01, 0x88, 0xB1, 0x01, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a
+ 0x0C, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x11, 0xF0, 0xFF, 0x17, 0x10, 0x00, 0x1C, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b
+ 0x05, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x00, 0xE0, 0xFF, 0x11, 0x80, 0x00, 0x17, 0x80, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c
+ 0x0C, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x13, 0x00, 0x00, 0xF6, 0xFF, 0x01, 0xF6, 0xFF, 0x01, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d
+ 0x07, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x00, 0xE0, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x58, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e
+ 0x0C, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
+ 0x05, 0x80, 0x08, 0x00, 0x80, 0xFF, 0x00, 0xE0, 0xFF, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x80, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x04, 0x00, 0x01, 0x04, 0x80, 0x01, 0x06, 0xC0, 0x00, 0xF0, 0x7F, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x01, 0x30, 0x00, 0x01, 0x10, 0x00, 0x01, 0x30, 0x00, 0x01, 0x20, 0x80, 0x00, 0xB0, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xC0, 0x00, 0x04, 0x80, 0x01, 0x04, 0x00, 0x01, 0x04, 0x00, 0x01, 0x04, 0x00, 0x01, 0x04, 0x80, 0x01, 0x00, 0xC0, 0x00, 0xF0, 0x7F, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0xA0, 0xFF, 0x01, 0x20, 0x00, 0x01, 0x20, 0x00, 0x01, 0x20, 0x00, 0x01, 0x20, 0x80, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x80, 0x01, 0x04, 0x00, 0x01, 0x04, 0x00, 0x01, 0x04, 0x00, 0x01, 0x03, 0x80, 0x01, 0x00, 0xC0, 0x00, 0xF0, 0x7F, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x01, 0x18, 0x00, 0x01, 0x20, 0x00, 0x01, 0x20, 0x00, 0x01, 0x20, 0x80, 0x00, 0x98, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x01, 0x0F, 0x00, 0x01, 0x09, 0x00, 0x01, 0x09, 0x00, 0x01, 0x06, 0x80, 0x01, 0x00, 0xC0, 0x00, 0xF0, 0x7F, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x3C, 0x00, 0x01, 0x24, 0x00, 0x01, 0x24, 0x80, 0x00, 0x98, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x7F, 0x00, 0x04, 0xC0, 0x00, 0x06, 0x80, 0x01, 0x03, 0x00, 0x01, 0x01, 0x00, 0x01, 0x04, 0x00, 0x01, 0x03, 0x80, 0x01, 0x01, 0xC0, 0x00, 0xF1, 0x7F, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p
+ 0x0A, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x01, 0x20, 0x00, 0x01, 0x30, 0x00, 0x01, 0x18, 0x00, 0x01, 0x28, 0x80, 0x00, 0xB0, 0xFF, 0x01, 0x98, 0xFF, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q
+ 0x0C, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x13, 0x00, 0x80, 0x11, 0x00, 0xC0, 0x00, 0xF0, 0x7F, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x1E, 0x80, 0xFF, 0x13, 0x80, 0xFF, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
+ 0x13, 0x10, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF8, 0x00, 0x04, 0x1F, 0x00, 0xE6, 0x03, 0x00, 0x33, 0x00, 0x00, 0xF3, 0x03, 0x00, 0x06, 0x1F, 0x00, 0x04, 0xF8, 0x00, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x01, 0x00, 0x7E, 0x00, 0xC0, 0x0F, 0x00, 0xF0, 0x00, 0x00, 0x10, 0x00, 0x00, // Code for char t
+ 0x0D, 0x80, 0x01, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x01, 0x20, 0xF0, 0x01, 0x90, 0x1F, 0x00, 0x98, 0x00, 0x00, 0x98, 0x1F, 0x00, 0x30, 0xF0, 0x01, 0x20, 0x80, 0x01, 0x00, 0xF8, 0x00, 0x80, 0x1F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u
+ 0x0B, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC4, 0x01, 0x00, 0x06, 0x07, 0x00, 0x03, 0xFE, 0x01, 0x03, 0xFE, 0x01, 0x06, 0x07, 0x00, 0xC4, 0x01, 0x00, 0x70, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v
+ 0x09, 0x80, 0x00, 0x00, 0x80, 0x07, 0x10, 0x20, 0x3E, 0x18, 0x10, 0xF0, 0x0C, 0x18, 0x80, 0x07, 0x18, 0xF0, 0x01, 0x30, 0x3E, 0x00, 0xA0, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char w
+ 0x0B, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0xC6, 0x01, 0x00, 0x06, 0x07, 0x00, 0x00, 0xFE, 0x01, 0x00, 0xFE, 0x01, 0x06, 0x07, 0x00, 0xC6, 0x01, 0x00, 0x70, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x
+ 0x0B, 0x00, 0x00, 0x00, 0x10, 0x80, 0x01, 0x10, 0xC0, 0x01, 0x10, 0x70, 0x01, 0x10, 0x18, 0x01, 0x14, 0x0C, 0x01, 0x13, 0x07, 0x01, 0x93, 0x01, 0x01, 0xD1, 0x00, 0x01, 0x70, 0x00, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x80, 0xE0, 0x01, 0x80, 0x70, 0x01, 0xA0, 0x1C, 0x01, 0x98, 0x0E, 0x01, 0x88, 0x03, 0x01, 0x80, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z
+ 0x0B, 0x00, 0x00, 0x00, 0x10, 0x80, 0x01, 0x10, 0xC0, 0x01, 0x10, 0x70, 0x01, 0x10, 0x18, 0x01, 0x16, 0x0C, 0x01, 0x16, 0x07, 0x01, 0x90, 0x01, 0x01, 0xD0, 0x00, 0x01, 0x70, 0x00, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char {
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x80, 0xE0, 0x01, 0x98, 0x70, 0x01, 0x98, 0x1C, 0x01, 0x80, 0x0E, 0x01, 0x80, 0x03, 0x01, 0x80, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
+ 0x0B, 0x00, 0x00, 0x00, 0x10, 0x80, 0x01, 0x10, 0xC0, 0x01, 0x11, 0x70, 0x01, 0x13, 0x18, 0x01, 0x16, 0x0C, 0x01, 0x16, 0x07, 0x01, 0x93, 0x01, 0x01, 0xD1, 0x00, 0x01, 0x70, 0x00, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x88, 0xE0, 0x01, 0x98, 0x70, 0x01, 0xB0, 0x1C, 0x01, 0xB0, 0x0E, 0x01, 0x98, 0x03, 0x01, 0x88, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~
+
+#if USE_CYRILLIC_CHARACTERS
+ // Cyrillic characters 0x401 to 0x491
+ 0x05, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x16, 0x04, 0x01, 0x16, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x16, 0x04, 0x01, 0x16, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x81, 0x00, 0x10, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x03, 0x01, 0x00, 0xFF, 0x01, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0xE0, 0xE4, 0x00, 0x30, 0x84, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x20, 0x80, 0x00, 0xE0, 0xC0, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x41, 0x00, 0xE0, 0xE3, 0x00, 0x30, 0x82, 0x01, 0x10, 0x06, 0x01, 0x10, 0x06, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x30, 0x8C, 0x01, 0x60, 0xF8, 0x00, 0x40, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x06, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x09, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x80, 0x01, 0xF0, 0xFF, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x00, 0xE0, 0x7F, 0x00, 0xF0, 0x03, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x86, 0x01, 0x00, 0xFC, 0x00, 0x00, 0x78, 0x00, // Code for char
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x86, 0x01, 0x00, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0xFF, 0x01, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x04, 0x04, 0x00, 0x06, 0x0F, 0x00, 0x83, 0x39, 0x00, 0xE1, 0xF0, 0x00, 0x30, 0xC0, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x70, 0x00, 0x01, 0xE3, 0x01, 0x01, 0x87, 0x83, 0x01, 0x04, 0xCE, 0x00, 0x04, 0x7C, 0x00, 0x04, 0x1C, 0x00, 0x07, 0x07, 0x00, 0xC3, 0x01, 0x00, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x01, 0x00, 0x78, 0x00, 0x00, 0x1F, 0x00, 0xE0, 0x13, 0x00, 0x70, 0x10, 0x00, 0x70, 0x10, 0x00, 0xE0, 0x13, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x8C, 0x01, 0x10, 0xF8, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x30, 0x06, 0x01, 0xE0, 0x8B, 0x01, 0xE0, 0xF9, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x01, 0x00, 0xFF, 0x01, 0xF0, 0x0F, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x30, 0xC0, 0x01, 0xE0, 0xE0, 0x00, 0x80, 0x39, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x71, 0x00, 0x60, 0xE0, 0x00, 0x30, 0x80, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, // Code for char 
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x60, 0xC0, 0x00, 0x30, 0x00, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x30, 0x8E, 0x01, 0xE0, 0xFB, 0x00, 0xC0, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x03, 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x03, 0xC0, 0x01, 0x07, 0x70, 0x00, 0x04, 0x38, 0x00, 0x04, 0x0E, 0x00, 0x84, 0x03, 0x00, 0xC7, 0x01, 0x00, 0x73, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0F, 0x00, 0x80, 0x39, 0x00, 0xE0, 0xF0, 0x00, 0x30, 0xC0, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0B, 0x00, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF0, 0x00, 0xE0, 0x7F, 0x00, 0xF0, 0x03, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0xE0, 0x01, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xE0, 0x01, 0x00, 0xE0, 0x01, 0x00, 0x7C, 0x00, 0x00, 0x0F, 0x00, 0xE0, 0x01, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xE0, 0x00, 0x30, 0x80, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x30, 0x80, 0x01, 0x60, 0xC0, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x30, 0x06, 0x00, 0xE0, 0x03, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xE0, 0x00, 0x30, 0x80, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x20, 0x80, 0x00, 0xE0, 0xC0, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
+ 0x0B, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
+ 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x70, 0x00, 0x01, 0xE0, 0x01, 0x01, 0x80, 0x83, 0x01, 0x00, 0xCE, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x07, 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char #
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x3F, 0x00, 0xC0, 0x60, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xC0, 0x60, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $
+ 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x30, 0x80, 0x01, 0x60, 0xE0, 0x00, 0xC0, 0x70, 0x00, 0x80, 0x1B, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x1B, 0x00, 0xC0, 0x70, 0x00, 0x60, 0xE0, 0x00, 0x30, 0x80, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char %
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char &
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, // Code for char )
+ 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x86, 0x01, 0x00, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char *
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x86, 0x01, 0x00, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char +
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x86, 0x01, 0x00, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0xE0, 0xE0, 0x00, 0x20, 0x80, 0x00, 0x10, 0x00, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x04, 0x01, 0x30, 0x84, 0x01, 0xE0, 0xE4, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char -
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1F, 0x00, 0xC0, 0x7F, 0x00, 0x60, 0xE0, 0x00, 0x30, 0x80, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x30, 0x80, 0x01, 0x60, 0xC0, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, // Code for char .
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x81, 0x01, 0xE0, 0xE3, 0x00, 0x30, 0x76, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x0C, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0x10, 0x04, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char /
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0xF3, 0x01, 0x80, 0x19, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x89, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0xC0, 0xFD, 0x00, 0x20, 0x83, 0x01, 0x30, 0x01, 0x01, 0x90, 0x01, 0x01, 0x90, 0x01, 0x01, 0x18, 0x81, 0x01, 0x18, 0xFF, 0x00, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0xF7, 0x01, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2
+ 0x07, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3
+ 0x0B, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x1F, 0x00, 0xF0, 0x01, 0x80, 0x3F, 0x01, 0x80, 0x03, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x89, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x89, 0x01, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5
+ 0x0E, 0x80, 0x00, 0x01, 0x80, 0xC1, 0x01, 0x00, 0x63, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x63, 0x00, 0x80, 0xC1, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x80, 0xC3, 0x01, 0x80, 0x89, 0x01, 0x80, 0x08, 0x01, 0x80, 0x1C, 0x01, 0x80, 0xF7, 0x01, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x03, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x98, 0xFF, 0x01, 0x38, 0xC0, 0x00, 0x20, 0x30, 0x00, 0x20, 0x0C, 0x00, 0x20, 0x03, 0x00, 0xB8, 0xFF, 0x01, 0x98, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x76, 0x00, 0x80, 0xC1, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
+ 0x09, 0x00, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF8, 0x00, 0x80, 0x3F, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
+ 0x0C, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x01, 0x00, 0xF0, 0x00, 0x00, 0x1E, 0x00, 0x80, 0x03, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char <
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char =
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ?
+ 0x0A, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x1F, 0x80, 0xFF, 0x1F, 0x00, 0x81, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char @
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xC3, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A
+ 0x08, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B
+ 0x09, 0x80, 0x01, 0x10, 0x80, 0x07, 0x10, 0x00, 0x3C, 0x18, 0x00, 0xF0, 0x0F, 0x00, 0x80, 0x07, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x80, 0x07, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x01, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x81, 0x00, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0x00, 0x81, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D
+ 0x09, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xE7, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xE7, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E
+ 0x0B, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G
+ 0x0D, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H
+ 0x0F, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
+ 0x0B, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x88, 0x01, 0x00, 0xF8, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J
+ 0x0D, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x88, 0x01, 0x00, 0xF8, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K
+ 0x0A, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x98, 0x01, 0x00, 0xF8, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0xC3, 0x00, 0x80, 0x81, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x89, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M
+ 0x0D, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x81, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N
+ 0x09, 0x00, 0x00, 0x01, 0x00, 0x8F, 0x01, 0x80, 0xDF, 0x00, 0x80, 0x70, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0xB0, 0x89, 0x01, 0xB0, 0x08, 0x01, 0x80, 0x08, 0x01, 0xB0, 0x08, 0x01, 0xB0, 0x89, 0x01, 0x00, 0xCF, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q
+ 0x09, 0x10, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x10, 0x01, 0x00, 0x90, 0x00, 0x00, 0x90, 0x00, 0x00, 0x80, 0x00, 0x10, 0x80, 0xFF, 0x1F, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char R
+ 0x07, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0xA0, 0x00, 0x00, 0x90, 0x00, 0x00, 0x98, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0x00, 0x80, 0x89, 0x01, 0x80, 0x08, 0x01, 0x80, 0x08, 0x01, 0x80, 0x81, 0x01, 0x00, 0xC3, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x80, 0xCF, 0x00, 0x80, 0x8C, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x19, 0x01, 0x00, 0xF3, 0x01, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U
+ 0x03, 0x00, 0x00, 0x00, 0x98, 0xFF, 0x01, 0x98, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V
+ 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xB0, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char W
+ 0x03, 0x00, 0x00, 0x10, 0x98, 0xFF, 0x1F, 0x98, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X
+ 0x10, 0x00, 0x00, 0x01, 0x00, 0x80, 0x01, 0x00, 0xF8, 0x00, 0x80, 0xFF, 0x00, 0x80, 0x0F, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0xF8, 0x01, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y
+ 0x0E, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x88, 0x01, 0x00, 0xF8, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z
+ 0x09, 0x10, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xF8, 0xFF, 0x01, 0x10, 0x01, 0x00, 0x90, 0x00, 0x00, 0x90, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x08, 0x00, 0x20, 0x1C, 0x00, 0x10, 0x76, 0x00, 0x98, 0xC1, 0x01, 0x88, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
+ 0x09, 0x80, 0x01, 0x10, 0x80, 0x07, 0x10, 0x18, 0x3C, 0x18, 0x38, 0xF0, 0x0F, 0x20, 0x80, 0x07, 0x20, 0xF0, 0x00, 0x20, 0x3C, 0x00, 0xB8, 0x07, 0x00, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^
+ 0x09, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char w
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char {
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char €
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‚
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ƒ
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char „
+ 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char …
+ 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char †
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‡
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ˆ
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‰
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Š
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‹
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Œ
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ž
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0x10, 0x00, 0x01, 0xF0, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0xF0, 0xFF, 0x01, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x08, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x80, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ‘
+#endif
+};
+
+extern const LcdFont font19x21 =
+{
+ Liberation_Sans19x21, // font data
+ 0x0020, // first character code
+#if USE_CYRILLIC_CHARACTERS
+ qq,
+#else
+ 0x017F, // last character code
+#endif
+ 21, // row height in pixels
+ 19, // character width in pixels
+ 2 // number of space columns between characters before kerning
+};
+
+#endif
+
+// End
diff --git a/src/Display/Lcd/Fonts/glcd28x32.cpp b/src/Display/Lcd/Fonts/glcd28x32.cpp
new file mode 100644
index 00000000..a4924531
--- /dev/null
+++ b/src/Display/Lcd/Fonts/glcd28x32.cpp
@@ -0,0 +1,549 @@
+/*
+ * glcd28x32_new.cpp
+ *
+ * Created: 04/03/2015 15:53:01
+ * Author: David
+ */
+
+//Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0
+//MikroElektronika 2011
+//http://www.mikroe.com
+
+//GLCD FontName : Liberation_Sans28x32
+//GLCD FontSize : 28 x 32
+
+#include "RepRapFirmware.h"
+
+#if SUPPORT_DIRECT_LCD
+
+#include "LcdFont.h"
+
+#define USE_CYRILLIC_CHARACTERS 0
+
+const uint8_t Liberation_Sans28x32[] =
+{
+ // ASCII 0x20 to 0x7F
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x03, 0xC0, 0xFF, 0x8F, 0x03, 0xC0, 0xFF, 0x8F, 0x03, 0xC0, 0x7F, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
+ 0x08, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
+ 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x98, 0x03, 0x00, 0x18, 0xFC, 0x01, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFF, 0x18, 0x00, 0xC0, 0x1B, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x02, 0x00, 0x18, 0xF8, 0x03, 0x00, 0x18, 0xFF, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0xC0, 0x1F, 0x18, 0x00, 0xC0, 0x19, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char #
+ 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1E, 0xE0, 0x00, 0x00, 0x3F, 0xF0, 0x01, 0x80, 0x7F, 0xC0, 0x01, 0xC0, 0xE1, 0x80, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xF0, 0xFF, 0xFF, 0x0F, 0xF0, 0xFF, 0xFF, 0x0F, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x81, 0x83, 0x03, 0x80, 0x03, 0x87, 0x01, 0x00, 0x07, 0xFF, 0x01, 0x00, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $
+ 0x18, 0x00, 0x7E, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0x81, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x81, 0x83, 0x03, 0x80, 0xFF, 0xE1, 0x01, 0x00, 0x7F, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0x00, 0x00, 0x87, 0xFF, 0x01, 0xC0, 0xC1, 0x81, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x81, 0x03, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char %
+ 0x12, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x80, 0x83, 0x03, 0x00, 0xBF, 0x01, 0x03, 0x80, 0xFF, 0x01, 0x03, 0xC0, 0xE1, 0x03, 0x03, 0xC0, 0xE0, 0x07, 0x03, 0xC0, 0x60, 0x1E, 0x03, 0xC0, 0x70, 0xB8, 0x01, 0xC0, 0x31, 0xF0, 0x01, 0x80, 0x1F, 0xE0, 0x00, 0x00, 0x0F, 0xF8, 0x01, 0x00, 0x00, 0xBE, 0x03, 0x00, 0x80, 0x0F, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char &
+ 0x02, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
+ 0x07, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x07, 0x00, 0xFE, 0xF1, 0x0F, 0x80, 0x0F, 0x00, 0x3E, 0xC0, 0x03, 0x00, 0x78, 0xE0, 0x00, 0x00, 0xE0, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
+ 0x07, 0x60, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x00, 0xE0, 0xC0, 0x03, 0x00, 0x78, 0x80, 0x0F, 0x00, 0x3E, 0x00, 0xFE, 0xF1, 0x0F, 0x00, 0xFC, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char )
+ 0x0B, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char *
+ 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char +
+ 0x02, 0x00, 0x00, 0x80, 0x33, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
+ 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char -
+ 0x02, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
+ 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xFC, 0x03, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char /
+ 0x0E, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0xE0, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0
+ 0x0D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0x07, 0x00, 0x03, 0x80, 0x03, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1
+ 0x0E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0xC0, 0x03, 0x80, 0x07, 0xE0, 0x03, 0x80, 0x03, 0xF0, 0x03, 0xC0, 0x01, 0x38, 0x03, 0xC0, 0x00, 0x1C, 0x03, 0xC0, 0x00, 0x0E, 0x03, 0xC0, 0x00, 0x06, 0x03, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x81, 0x03, 0x03, 0x80, 0xE3, 0x01, 0x03, 0x80, 0xFF, 0x00, 0x03, 0x00, 0x3F, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2
+ 0x0E, 0x00, 0x00, 0x20, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x80, 0x07, 0xF0, 0x01, 0x80, 0x03, 0x80, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0xC1, 0x83, 0x03, 0x80, 0x77, 0x87, 0x01, 0x80, 0x7F, 0xFE, 0x01, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3
+ 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x1B, 0x00, 0x00, 0xC0, 0x19, 0x00, 0x00, 0xF0, 0x18, 0x00, 0x00, 0x38, 0x18, 0x00, 0x00, 0x1E, 0x18, 0x00, 0x00, 0x07, 0x18, 0x00, 0xC0, 0x03, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4
+ 0x0E, 0x00, 0x00, 0x20, 0x00, 0xC0, 0xFF, 0xE0, 0x00, 0xC0, 0xFF, 0xE0, 0x01, 0xC0, 0xCF, 0x80, 0x01, 0xC0, 0xC0, 0x80, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0xE0, 0x80, 0x03, 0xC0, 0xC0, 0xC1, 0x01, 0xC0, 0xC0, 0xFF, 0x01, 0xC0, 0x80, 0xFF, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5
+ 0x0D, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x80, 0x8F, 0xE1, 0x01, 0x80, 0xC1, 0x80, 0x01, 0xC0, 0x61, 0x00, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0x60, 0x00, 0x03, 0xC0, 0xE1, 0x80, 0x03, 0x80, 0xC3, 0xC1, 0x01, 0x80, 0xC3, 0xFF, 0x01, 0x00, 0x83, 0xFF, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6
+ 0x0E, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xC0, 0x03, 0xC0, 0x00, 0xFC, 0x03, 0xC0, 0x00, 0xFF, 0x03, 0xC0, 0xC0, 0x0F, 0x00, 0xC0, 0xF0, 0x01, 0x00, 0xC0, 0x7C, 0x00, 0x00, 0xC0, 0x1E, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7
+ 0x0E, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x80, 0x7F, 0xFE, 0x01, 0x80, 0xE3, 0x83, 0x01, 0xC0, 0xC1, 0x83, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0xC1, 0x03, 0x03, 0x80, 0xE3, 0x83, 0x01, 0x80, 0x7F, 0xFE, 0x01, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8
+ 0x0E, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0xC1, 0x00, 0x80, 0xFF, 0xC3, 0x01, 0x80, 0x83, 0xC3, 0x01, 0xC0, 0x01, 0x87, 0x03, 0xC0, 0x00, 0x06, 0x03, 0xC0, 0x00, 0x06, 0x03, 0xC0, 0x00, 0x06, 0x03, 0xC0, 0x00, 0x86, 0x03, 0xC0, 0x01, 0x83, 0x01, 0x80, 0x87, 0xE1, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9
+ 0x02, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
+ 0x02, 0x00, 0x1C, 0x80, 0x33, 0x00, 0x1C, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
+ 0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x70, 0x1C, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x0C, 0x60, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char <
+ 0x0F, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char =
+ 0x0F, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x0C, 0x60, 0x00, 0x00, 0x1C, 0x60, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x70, 0x1C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
+ 0x0E, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x88, 0x03, 0xC0, 0x00, 0x8E, 0x03, 0xC0, 0x00, 0x8F, 0x03, 0xC0, 0x80, 0x03, 0x00, 0xC0, 0x80, 0x01, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ?
+ 0x19, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x01, 0x00, 0xF8, 0xE0, 0x03, 0x00, 0x1C, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x81, 0x7F, 0x18, 0xC0, 0xE1, 0xFF, 0x18, 0xC0, 0xF0, 0xC0, 0x31, 0xC0, 0x38, 0x80, 0x31, 0x60, 0x1C, 0x80, 0x31, 0x60, 0x0C, 0x80, 0x31, 0x60, 0x0C, 0xC0, 0x30, 0x60, 0x0C, 0xC0, 0x30, 0x60, 0x0C, 0x70, 0x30, 0x60, 0x18, 0xFC, 0x38, 0x60, 0xF0, 0xFF, 0x19, 0xE0, 0xFC, 0xCF, 0x19, 0xC0, 0x7C, 0x80, 0x1D, 0xC0, 0x05, 0x80, 0x0D, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x3E, 0x7E, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char @
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x80, 0x3F, 0x0C, 0x00, 0xC0, 0x07, 0x0C, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0xC0, 0x07, 0x0C, 0x00, 0x80, 0x3F, 0x0C, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A
+ 0x0F, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xE1, 0x00, 0x03, 0x80, 0xF3, 0x81, 0x03, 0x80, 0xBF, 0xC3, 0x01, 0x00, 0x1F, 0xFF, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B
+ 0x13, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x80, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xC0, 0x01, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0E, 0x70, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C
+ 0x12, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E
+ 0x0F, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F
+ 0x14, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x01, 0x03, 0x03, 0x80, 0x01, 0x83, 0x03, 0x80, 0x03, 0x83, 0x01, 0x80, 0x07, 0xC3, 0x01, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x04, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H
+ 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
+ 0x0D, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xB8, 0x07, 0x00, 0x00, 0x1C, 0x0F, 0x00, 0x00, 0x0E, 0x1E, 0x00, 0x00, 0x07, 0x38, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x01, 0xE0, 0x01, 0xC0, 0x00, 0xC0, 0x03, 0x40, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K
+ 0x0D, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L
+ 0x13, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O
+ 0x0F, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x81, 0x03, 0x00, 0x80, 0xC3, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x73, 0x80, 0x01, 0x80, 0x61, 0x80, 0x03, 0xC0, 0x61, 0x00, 0x07, 0xE0, 0x60, 0x00, 0x1F, 0xF8, 0x60, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x0F, 0x00, 0xC0, 0x00, 0x1F, 0x00, 0xC0, 0x81, 0x7F, 0x00, 0x80, 0xC3, 0xF1, 0x00, 0x80, 0xFF, 0xE1, 0x03, 0x00, 0xFF, 0x80, 0x03, 0x00, 0x3C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char R
+ 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1E, 0x60, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x80, 0x7F, 0xC0, 0x01, 0x80, 0xE1, 0x80, 0x01, 0xC0, 0xE1, 0x80, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x01, 0x03, 0xC0, 0xC0, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x03, 0x03, 0xC0, 0x81, 0x83, 0x01, 0x80, 0x07, 0xC7, 0x01, 0x00, 0x07, 0xFF, 0x01, 0x00, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S
+ 0x11, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
+ 0x11, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U
+ 0x13, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V
+ 0x1C, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, // Code for char W
+ 0x11, 0x00, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x03, 0xC0, 0x07, 0xE0, 0x01, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x3C, 0x3E, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x80, 0x07, 0xF0, 0x01, 0xC0, 0x01, 0xC0, 0x03, 0xC0, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X
+ 0x11, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x80, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y
+ 0x10, 0x00, 0x00, 0x80, 0x03, 0xC0, 0x00, 0xC0, 0x03, 0xC0, 0x00, 0xE0, 0x03, 0xC0, 0x00, 0x70, 0x03, 0xC0, 0x00, 0x3C, 0x03, 0xC0, 0x00, 0x1E, 0x03, 0xC0, 0x00, 0x07, 0x03, 0xC0, 0xC0, 0x03, 0x03, 0xC0, 0xE0, 0x01, 0x03, 0xC0, 0xF0, 0x00, 0x03, 0xC0, 0x3C, 0x00, 0x03, 0xC0, 0x1E, 0x00, 0x03, 0xC0, 0x0F, 0x00, 0x03, 0xC0, 0x03, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z
+ 0x06, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
+ 0x08, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash
+ 0x06, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
+ 0x0E, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^
+ 0x10, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _
+ 0x06, 0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x00, 0x38, 0x86, 0x03, 0x00, 0x0C, 0x07, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x0C, 0x83, 0x01, 0x00, 0x1C, 0xC3, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a
+ 0x0D, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0xE0, 0x79, 0x00, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b
+ 0x0D, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x78, 0xC0, 0x01, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c
+ 0x0D, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0xE0, 0x79, 0x00, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x38, 0xC6, 0x01, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x1C, 0x06, 0x03, 0x00, 0x38, 0x86, 0x01, 0x00, 0xF0, 0xC7, 0x01, 0x00, 0xE0, 0xC7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e
+ 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x60, 0x0C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
+ 0x0D, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0xFF, 0x30, 0x00, 0xF8, 0xFF, 0x71, 0x00, 0x3C, 0xC0, 0x73, 0x00, 0x1C, 0x80, 0xE3, 0x00, 0x0C, 0x00, 0xC3, 0x00, 0x0C, 0x00, 0xC3, 0x00, 0x0C, 0x00, 0xC3, 0x00, 0x18, 0x80, 0xC1, 0x00, 0x38, 0xC0, 0xE0, 0x00, 0xE0, 0x79, 0x7C, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g
+ 0x0C, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h
+ 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
+ 0x05, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x60, 0xFC, 0xFF, 0xFF, 0x60, 0xFC, 0xFF, 0x7F, 0x60, 0xFC, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
+ 0x0C, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3C, 0x00, 0x00, 0x70, 0xF8, 0x00, 0x00, 0x3C, 0xE0, 0x01, 0x00, 0x1C, 0xC0, 0x03, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k
+ 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
+ 0x15, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m
+ 0x0C, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x38, 0xC0, 0x01, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o
+ 0x0D, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0xE0, 0x79, 0x00, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p
+ 0x0D, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x08, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0xE0, 0x79, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q
+ 0x08, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r
+ 0x0D, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xF0, 0xC1, 0x01, 0x00, 0xF8, 0x83, 0x01, 0x00, 0x9C, 0x83, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x07, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x1C, 0x8E, 0x03, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x20, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
+ 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t
+ 0x0C, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0xFC, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u
+ 0x0D, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v
+ 0x15, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFC, 0x07, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xFC, 0x03, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xFC, 0x07, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char w
+ 0x0B, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0xC0, 0x03, 0x00, 0x7C, 0xF0, 0x01, 0x00, 0xF0, 0x79, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xF0, 0x79, 0x00, 0x00, 0x7C, 0xF0, 0x01, 0x00, 0x1C, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x
+ 0x0D, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0xFC, 0x01, 0xC0, 0x00, 0xF0, 0x0F, 0xE0, 0x00, 0x00, 0x7F, 0x70, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
+ 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0xC0, 0x03, 0x00, 0x0C, 0xE0, 0x03, 0x00, 0x0C, 0x78, 0x03, 0x00, 0x0C, 0x3C, 0x03, 0x00, 0x0C, 0x1F, 0x03, 0x00, 0x8C, 0x07, 0x03, 0x00, 0xEC, 0x03, 0x03, 0x00, 0xFC, 0x00, 0x03, 0x00, 0x7C, 0x00, 0x03, 0x00, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z
+ 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0xC0, 0xFF, 0xF9, 0x7F, 0xE0, 0xFF, 0xF0, 0xFF, 0xE0, 0x00, 0x00, 0xE0, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char {
+ 0x02, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
+ 0x09, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x00, 0xE0, 0xE0, 0xFF, 0xF1, 0xFF, 0xC0, 0xFF, 0xFD, 0x7F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
+ 0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~
+ 0x05, 0x80, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char DEL
+
+ // Custom characters inserted at 0x80 to 0x86
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for thin space
+ 0x1A, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x3B, 0x00, 0x00, 0x38, 0x73, 0x00, 0x00, 0x08, 0x43, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for left arrow 
+ 0x0C, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0x1F, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for up arrow
+ 0x1A, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x43, 0x00, 0x00, 0x38, 0x73, 0x00, 0x00, 0x70, 0x3B, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for right arrow
+ 0x0C, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1C, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for down arrow
+ 0x0E, 0x00, 0x01, 0x80, 0x00, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0xFC, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, 0x3F, 0x18, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0C, 0x60, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for more arrow
+ 0x0E, 0x80, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x18, 0x18, 0x00, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0x00, 0x18, 0x18, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for less arrow
+
+ // Non-latin characters 0x87 to 0x17F
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‡
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ˆ
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‰
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Š
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‹
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Œ
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ž
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0x90
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‘
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ’
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char “
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ”
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char •
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char –
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char —
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ˜
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ™
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char š
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ›
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char œ
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char �
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ž
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ÿ
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0xA0
+ 0x03, 0x00, 0x1C, 0xFE, 0x7F, 0x00, 0x1C, 0xFF, 0x7F, 0x00, 0x1C, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¡
+ 0x0C, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0xC0, 0x03, 0xC0, 0x03, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0E, 0x70, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x18, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¢
+ 0x0E, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x81, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFF, 0x7F, 0x03, 0x80, 0xFF, 0x3F, 0x03, 0xC0, 0x81, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x81, 0x01, 0x03, 0x80, 0x03, 0x80, 0x03, 0x00, 0x03, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char £
+ 0x0C, 0x00, 0xEC, 0x37, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x38, 0x1C, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x38, 0x1C, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xEC, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¤
+ 0x10, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x33, 0x00, 0xC0, 0x03, 0x33, 0x00, 0x80, 0x0F, 0x33, 0x00, 0x00, 0x3E, 0x33, 0x00, 0x00, 0x78, 0x33, 0x00, 0x00, 0xE0, 0x33, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xE0, 0x33, 0x00, 0x00, 0x78, 0x33, 0x00, 0x00, 0x3E, 0x33, 0x00, 0x80, 0x0F, 0x33, 0x00, 0xC0, 0x03, 0x33, 0x00, 0xC0, 0x01, 0x33, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¥
+ 0x02, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¦
+ 0x0C, 0x00, 0x83, 0x03, 0x03, 0xC0, 0xC7, 0x07, 0x07, 0xC0, 0x6F, 0x0E, 0x06, 0xE0, 0x3C, 0x0C, 0x0C, 0x60, 0x38, 0x0C, 0x0C, 0x60, 0x18, 0x18, 0x0C, 0x60, 0x30, 0x18, 0x0C, 0x60, 0x30, 0x18, 0x0C, 0x60, 0x30, 0x38, 0x0C, 0xC0, 0x70, 0x3C, 0x06, 0xC0, 0xE1, 0xF7, 0x07, 0x80, 0xC1, 0xE7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char §
+ 0x08, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¨
+ 0x15, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x01, 0x80, 0x00, 0x80, 0xE1, 0x83, 0x01, 0x80, 0xF0, 0x0F, 0x01, 0xC0, 0x18, 0x18, 0x03, 0x40, 0x0C, 0x30, 0x02, 0x40, 0x04, 0x20, 0x02, 0x40, 0x04, 0x20, 0x02, 0x40, 0x04, 0x20, 0x02, 0x40, 0x0C, 0x30, 0x02, 0xC0, 0x18, 0x18, 0x03, 0x80, 0x10, 0x08, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x01, 0x80, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ©
+ 0x0B, 0x00, 0x60, 0x00, 0x00, 0x80, 0xF9, 0x00, 0x00, 0x80, 0xFD, 0x01, 0x00, 0xC0, 0x0C, 0x01, 0x00, 0x40, 0x04, 0x01, 0x00, 0x40, 0x04, 0x01, 0x00, 0x40, 0x84, 0x00, 0x00, 0xC0, 0x65, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ª
+ 0x0E, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x73, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x3B, 0x00, 0x00, 0xC0, 0x71, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char «
+ 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¬
+ 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ­
+ 0x15, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x01, 0x80, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x01, 0xC0, 0xFC, 0x3F, 0x03, 0x40, 0x04, 0x01, 0x02, 0x40, 0x04, 0x01, 0x02, 0x40, 0x04, 0x01, 0x02, 0x40, 0x04, 0x07, 0x02, 0x40, 0x8C, 0x1F, 0x02, 0xC0, 0xF8, 0x38, 0x03, 0x80, 0x70, 0x20, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x01, 0x80, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ®
+ 0x10, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¯
+ 0x08, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ° (0xB0)
+ 0x0E, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xFF, 0x3F, 0x03, 0x00, 0xFF, 0x3F, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ±
+ 0x08, 0x00, 0x01, 0x03, 0x00, 0x80, 0x81, 0x03, 0x00, 0xC0, 0xC0, 0x02, 0x00, 0x40, 0x60, 0x02, 0x00, 0x40, 0x30, 0x02, 0x00, 0x40, 0x38, 0x02, 0x00, 0xC0, 0x1F, 0x02, 0x00, 0x80, 0x0F, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ²
+ 0x08, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x03, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x08, 0x02, 0x00, 0x40, 0x08, 0x02, 0x00, 0x40, 0x0C, 0x02, 0x00, 0xC0, 0xF7, 0x03, 0x00, 0x80, 0xF3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ³
+ 0x06, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ´
+ 0x0E, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char µ
+ 0x0E, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0xC0, 0xFF, 0xFF, 0x3F, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0xC0, 0xFF, 0xFF, 0x3F, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¶
+ 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ·
+ 0x05, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¸
+ 0x08, 0x00, 0x03, 0x02, 0x00, 0x80, 0x01, 0x02, 0x00, 0x80, 0x00, 0x02, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¹
+ 0x0B, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x00, 0xC0, 0x80, 0x01, 0x00, 0x40, 0x00, 0x01, 0x00, 0x40, 0x00, 0x01, 0x00, 0x40, 0x00, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0x80, 0xF7, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char º
+ 0x0E, 0x00, 0x20, 0x80, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0xC0, 0x71, 0x00, 0x00, 0x80, 0x3B, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0x80, 0x33, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char »
+ 0x16, 0x00, 0x03, 0x02, 0x00, 0x80, 0x01, 0x02, 0x00, 0x80, 0x00, 0x02, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0xC2, 0x03, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x40, 0x00, 0x00, 0x38, 0x70, 0x00, 0x00, 0x1C, 0x58, 0x00, 0x00, 0x07, 0x4E, 0x00, 0xC0, 0x03, 0x47, 0x00, 0xC0, 0x80, 0x41, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¼
+ 0x16, 0x00, 0x03, 0x02, 0x00, 0x80, 0x01, 0x02, 0x00, 0x80, 0x00, 0x02, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0xC2, 0x03, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x03, 0x00, 0x87, 0x81, 0x03, 0xC0, 0xC3, 0xC0, 0x02, 0xC0, 0x40, 0x60, 0x02, 0x00, 0x40, 0x30, 0x02, 0x00, 0x40, 0x38, 0x02, 0x00, 0xC0, 0x1F, 0x02, 0x00, 0x80, 0x0F, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ½
+ 0x15, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x03, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x08, 0x02, 0x02, 0x40, 0x08, 0x02, 0x03, 0x40, 0x0C, 0xC2, 0x03, 0xC0, 0xF7, 0xE3, 0x00, 0x80, 0xF3, 0x39, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x40, 0x00, 0x00, 0x38, 0x70, 0x00, 0x00, 0x1C, 0x58, 0x00, 0x00, 0x07, 0x4E, 0x00, 0xC0, 0x03, 0x47, 0x00, 0xC0, 0x80, 0x41, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¾
+ 0x0E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xF0, 0x3E, 0x00, 0x00, 0x38, 0x30, 0x00, 0x00, 0x1C, 0x60, 0x00, 0x00, 0x0E, 0x60, 0x00, 0x9C, 0x07, 0x60, 0x00, 0x9C, 0x07, 0x60, 0x00, 0x9C, 0x01, 0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ¿
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x82, 0x3F, 0x0C, 0x00, 0xC2, 0x07, 0x0C, 0x00, 0xC6, 0x00, 0x0C, 0x00, 0xCC, 0x07, 0x0C, 0x00, 0x88, 0x3F, 0x0C, 0x00, 0x08, 0xFC, 0x0C, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char À
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x80, 0x3F, 0x0C, 0x00, 0xC8, 0x07, 0x0C, 0x00, 0xC8, 0x00, 0x0C, 0x00, 0xCC, 0x07, 0x0C, 0x00, 0x86, 0x3F, 0x0C, 0x00, 0x02, 0xFC, 0x0C, 0x00, 0x02, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Á
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x08, 0xF0, 0x0F, 0x00, 0x0C, 0xFC, 0x0C, 0x00, 0x86, 0x3F, 0x0C, 0x00, 0xC7, 0x07, 0x0C, 0x00, 0xC3, 0x00, 0x0C, 0x00, 0xC3, 0x07, 0x0C, 0x00, 0x87, 0x3F, 0x0C, 0x00, 0x0C, 0xFC, 0x0C, 0x00, 0x08, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Â
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x0E, 0xF0, 0x0F, 0x00, 0x07, 0xFC, 0x0C, 0x00, 0x83, 0x3F, 0x0C, 0x00, 0xC3, 0x07, 0x0C, 0x00, 0xC6, 0x00, 0x0C, 0x00, 0xC6, 0x07, 0x0C, 0x00, 0x8C, 0x3F, 0x0C, 0x00, 0x0C, 0xFC, 0x0C, 0x00, 0x0E, 0xF0, 0x0F, 0x00, 0x07, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ã
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x0C, 0xFC, 0x0C, 0x00, 0x8C, 0x3F, 0x0C, 0x00, 0xC0, 0x07, 0x0C, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0xC0, 0x07, 0x0C, 0x00, 0x8C, 0x3F, 0x0C, 0x00, 0x0C, 0xFC, 0x0C, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ä
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x7C, 0xFC, 0x0C, 0x00, 0xEE, 0x3F, 0x0C, 0x00, 0xC6, 0x07, 0x0C, 0x00, 0xC6, 0x00, 0x0C, 0x00, 0xC6, 0x07, 0x0C, 0x00, 0xFC, 0x3F, 0x0C, 0x00, 0x38, 0xFC, 0x0C, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Å
+ 0x1B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x0D, 0x00, 0x00, 0x7E, 0x0C, 0x00, 0x80, 0x0F, 0x0C, 0x00, 0xC0, 0x03, 0x0C, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, // Code for char Æ
+ 0x13, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x80, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0xC3, 0xC0, 0x00, 0x00, 0xCF, 0xC0, 0x00, 0x00, 0xCF, 0xC0, 0x00, 0x00, 0xFB, 0xC0, 0x00, 0x00, 0x73, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xC0, 0x01, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0E, 0x70, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ç
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC2, 0x80, 0x01, 0x03, 0xC2, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC8, 0x80, 0x01, 0x03, 0xC8, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char È
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC8, 0x80, 0x01, 0x03, 0xC8, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC2, 0x80, 0x01, 0x03, 0xC2, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char É
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC8, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC7, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC7, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC8, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ê
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ë
+ 0x06, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC6, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0xC8, 0xFF, 0xFF, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ì
+ 0x06, 0xC8, 0xFF, 0xFF, 0x03, 0xC8, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Í
+ 0x08, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0xFF, 0xFF, 0x03, 0xC3, 0xFF, 0xFF, 0x03, 0xC3, 0xFF, 0xFF, 0x03, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Î
+ 0x07, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ï
+ 0x14, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ð
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0x00, 0x00, 0x0E, 0x0F, 0x00, 0x00, 0x07, 0x1E, 0x00, 0x00, 0x03, 0x78, 0x00, 0x00, 0x03, 0xF0, 0x01, 0x00, 0x06, 0xC0, 0x03, 0x00, 0x06, 0x80, 0x0F, 0x00, 0x0C, 0x00, 0x1E, 0x00, 0x0C, 0x00, 0x78, 0x00, 0x0E, 0x00, 0xF0, 0x01, 0x07, 0x00, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ñ
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC2, 0x00, 0x80, 0x03, 0xC2, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC8, 0x00, 0x00, 0x03, 0xC8, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ò
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC8, 0x00, 0x00, 0x03, 0xC8, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC2, 0x00, 0x00, 0x03, 0xC2, 0x00, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ó
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x88, 0x01, 0x80, 0x01, 0xCC, 0x00, 0x80, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0x88, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ô
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x8E, 0x01, 0x80, 0x01, 0xC7, 0x00, 0x80, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0x8E, 0x01, 0x80, 0x01, 0x87, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Õ
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xCC, 0x00, 0x80, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ö
+ 0x0D, 0x00, 0x08, 0x20, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x70, 0x1C, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0x70, 0x1C, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ×
+ 0x15, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x1F, 0x02, 0x00, 0xFE, 0x7F, 0x03, 0x00, 0x1F, 0xF8, 0x03, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x03, 0xF0, 0x01, 0x80, 0x01, 0xB8, 0x01, 0xC0, 0x00, 0x9C, 0x03, 0xC0, 0x00, 0x0E, 0x03, 0xC0, 0x00, 0x07, 0x03, 0xC0, 0xC0, 0x03, 0x03, 0xC0, 0xE0, 0x00, 0x03, 0xC0, 0x70, 0x00, 0x03, 0xC0, 0x39, 0x00, 0x03, 0x80, 0x1D, 0x80, 0x01, 0x80, 0x0F, 0xC0, 0x01, 0x00, 0x07, 0xE0, 0x00, 0xC0, 0x1F, 0xF8, 0x00, 0xE0, 0xFE, 0x7F, 0x00, 0x60, 0xF8, 0x1F, 0x00, 0x20, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ø
+ 0x11, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ù
+ 0x11, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ú
+ 0x11, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x08, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x80, 0x03, 0x06, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x80, 0x03, 0x08, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Û
+ 0x11, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x80, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ü
+ 0x11, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x08, 0xF0, 0x01, 0x00, 0x08, 0xC0, 0xFF, 0x03, 0x0C, 0x80, 0xFF, 0x03, 0x06, 0xC0, 0xFF, 0x03, 0x02, 0xF0, 0x01, 0x00, 0x02, 0x78, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ý
+ 0x0F, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x1C, 0x38, 0x00, 0x00, 0x38, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Þ
+ 0x0F, 0x00, 0xFE, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x80, 0x03, 0x60, 0xE0, 0x00, 0x03, 0x60, 0xF0, 0x03, 0x03, 0xE0, 0xB8, 0x03, 0x03, 0xC0, 0x1F, 0x07, 0x03, 0xC0, 0x0F, 0x0E, 0x03, 0x00, 0x07, 0xFC, 0x01, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ß
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x00, 0x38, 0x86, 0x03, 0x10, 0x0C, 0x07, 0x03, 0x30, 0x0C, 0x03, 0x03, 0x70, 0x0C, 0x03, 0x03, 0x60, 0x0C, 0x03, 0x01, 0xC0, 0x0C, 0x83, 0x01, 0x80, 0x1C, 0xC3, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char à
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x00, 0x38, 0x86, 0x03, 0x00, 0x0C, 0x07, 0x03, 0x80, 0x0C, 0x03, 0x03, 0xC0, 0x0C, 0x03, 0x03, 0x60, 0x0C, 0x03, 0x01, 0x30, 0x0C, 0x83, 0x01, 0x30, 0x1C, 0xC3, 0x00, 0x10, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char á
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x80, 0x38, 0xFE, 0x03, 0xC0, 0x38, 0x86, 0x03, 0x60, 0x0C, 0x07, 0x03, 0x70, 0x0C, 0x03, 0x03, 0x30, 0x0C, 0x03, 0x03, 0x30, 0x0C, 0x03, 0x01, 0x70, 0x0C, 0x83, 0x01, 0xE0, 0x1C, 0xC3, 0x00, 0xC0, 0xF8, 0xFF, 0x00, 0x80, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char â
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0xE0, 0x38, 0x86, 0x03, 0x30, 0x0C, 0x07, 0x03, 0x30, 0x0C, 0x03, 0x03, 0x30, 0x0C, 0x03, 0x03, 0x60, 0x0C, 0x03, 0x01, 0xC0, 0x0C, 0x83, 0x01, 0xC0, 0x1C, 0xC3, 0x00, 0xE0, 0xF8, 0xFF, 0x00, 0x70, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ã
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0xC0, 0x38, 0x86, 0x03, 0xC0, 0x0C, 0x07, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x0C, 0x83, 0x01, 0xC0, 0x1C, 0xC3, 0x00, 0xC0, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ä
+ 0x0F, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x00, 0x38, 0x86, 0x03, 0x7C, 0x0C, 0x07, 0x03, 0xEE, 0x0C, 0x03, 0x03, 0xC6, 0x0C, 0x03, 0x03, 0xC6, 0x0C, 0x03, 0x01, 0xC6, 0x0C, 0x83, 0x01, 0x7C, 0x1C, 0xC3, 0x00, 0x38, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char å
+ 0x18, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x83, 0x01, 0x00, 0x1C, 0xC3, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC6, 0x01, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x1C, 0x06, 0x03, 0x00, 0x38, 0x86, 0x01, 0x00, 0xF0, 0xC7, 0x01, 0x00, 0xE0, 0xC7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char æ
+ 0x0D, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0xC3, 0x00, 0x0C, 0x00, 0xCF, 0x00, 0x0C, 0x00, 0xCF, 0x00, 0x0C, 0x00, 0xFB, 0x00, 0x1C, 0x80, 0x73, 0x00, 0x78, 0xC0, 0x01, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ç
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x38, 0xC6, 0x01, 0x10, 0x1C, 0x86, 0x03, 0x30, 0x0C, 0x06, 0x03, 0x70, 0x0C, 0x06, 0x03, 0x60, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0x80, 0x1C, 0x06, 0x03, 0x00, 0x38, 0x86, 0x01, 0x00, 0xF0, 0xC7, 0x01, 0x00, 0xE0, 0xC7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char è
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x38, 0xC6, 0x01, 0x00, 0x1C, 0x86, 0x03, 0x80, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0x60, 0x0C, 0x06, 0x03, 0x30, 0x0C, 0x06, 0x03, 0x30, 0x1C, 0x06, 0x03, 0x10, 0x38, 0x86, 0x01, 0x00, 0xF0, 0xC7, 0x01, 0x00, 0xE0, 0xC7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char é
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0xF0, 0xFF, 0x00, 0xC0, 0x38, 0xC6, 0x01, 0x60, 0x1C, 0x86, 0x03, 0x70, 0x0C, 0x06, 0x03, 0x30, 0x0C, 0x06, 0x03, 0x30, 0x0C, 0x06, 0x03, 0x70, 0x0C, 0x06, 0x03, 0xE0, 0x1C, 0x06, 0x03, 0xC0, 0x38, 0x86, 0x01, 0x80, 0xF0, 0xC7, 0x01, 0x00, 0xE0, 0xC7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ê
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x38, 0xC6, 0x01, 0xC0, 0x1C, 0x86, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x1C, 0x06, 0x03, 0xC0, 0x38, 0x86, 0x01, 0xC0, 0xF0, 0xC7, 0x01, 0x00, 0xE0, 0xC7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ë
+ 0x06, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0xC0, 0xFC, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ì
+ 0x06, 0x80, 0x00, 0x00, 0x00, 0xC0, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x30, 0xFC, 0xFF, 0x03, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char í
+ 0x0A, 0x80, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x70, 0xFC, 0xFF, 0x03, 0x30, 0xFC, 0xFF, 0x03, 0x30, 0xFC, 0xFF, 0x03, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char î
+ 0x08, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ï
+ 0x0E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xE0, 0xFF, 0x01, 0x20, 0xE3, 0xC0, 0x01, 0x20, 0x71, 0x80, 0x03, 0x60, 0x31, 0x00, 0x03, 0xC0, 0x31, 0x00, 0x03, 0xC0, 0x31, 0x00, 0x03, 0xC0, 0x33, 0x00, 0x03, 0x40, 0x37, 0x80, 0x03, 0x40, 0x7E, 0x80, 0x01, 0x60, 0xFC, 0xFF, 0x01, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ð
+ 0x0C, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0xE0, 0xE0, 0xFF, 0x03, 0x30, 0x18, 0x00, 0x00, 0x30, 0x08, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xE0, 0x7C, 0x00, 0x00, 0x70, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ñ
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC0, 0x01, 0x10, 0x1C, 0x80, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x70, 0x0C, 0x00, 0x03, 0x60, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0x80, 0x1C, 0x80, 0x03, 0x00, 0x38, 0xC0, 0x01, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ò
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0x80, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0x60, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x30, 0x1C, 0x80, 0x03, 0x10, 0x38, 0xC0, 0x01, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ó
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0xF8, 0xFF, 0x01, 0xC0, 0x38, 0xC0, 0x01, 0x60, 0x1C, 0x80, 0x03, 0x70, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x70, 0x0C, 0x00, 0x03, 0xE0, 0x1C, 0x80, 0x03, 0xC0, 0x38, 0xC0, 0x01, 0x80, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ô
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xE0, 0x38, 0xC0, 0x01, 0x30, 0x1C, 0x80, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x60, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x1C, 0x80, 0x03, 0xE0, 0x38, 0xC0, 0x01, 0x70, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char õ
+ 0x0E, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC0, 0x01, 0xC0, 0x1C, 0x80, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0xC0, 0x38, 0xC0, 0x01, 0xC0, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ö
+ 0x0E, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9C, 0x39, 0x00, 0x00, 0x9C, 0x39, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ÷
+ 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x1F, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xE0, 0x01, 0x00, 0x1C, 0xB8, 0x03, 0x00, 0x0C, 0x1C, 0x03, 0x00, 0x0C, 0x0E, 0x03, 0x00, 0x0C, 0x07, 0x03, 0x00, 0x8C, 0x03, 0x03, 0x00, 0xDC, 0x81, 0x03, 0x00, 0x78, 0xC0, 0x01, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xCC, 0x3F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ø
+ 0x0C, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x10, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x01, 0x80, 0x00, 0x80, 0x01, 0x00, 0xFC, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ù
+ 0x0C, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x03, 0x30, 0x00, 0x80, 0x01, 0x30, 0x00, 0x80, 0x01, 0x10, 0xFC, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ú
+ 0x0C, 0x00, 0xFC, 0xFF, 0x00, 0x80, 0xFC, 0xFF, 0x01, 0xC0, 0x00, 0xE0, 0x03, 0x60, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x70, 0x00, 0x80, 0x01, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0xFC, 0x7F, 0x00, 0x80, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char û
+ 0x0C, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0xC0, 0x00, 0xE0, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0xFC, 0x7F, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ü
+ 0x0D, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0xFC, 0x01, 0xC0, 0x00, 0xF0, 0x0F, 0xE0, 0x80, 0x00, 0x7F, 0x70, 0xC0, 0x00, 0xF8, 0x7F, 0x60, 0x00, 0x80, 0x1F, 0x30, 0x00, 0xF0, 0x07, 0x30, 0x00, 0xFF, 0x00, 0x10, 0xE0, 0x1F, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ý
+ 0x0D, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x79, 0x00, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char þ
+ 0x0D, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0xFC, 0x01, 0xC0, 0xC0, 0xF0, 0x0F, 0xE0, 0xC0, 0x00, 0x7F, 0x70, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFF, 0x00, 0xC0, 0xE0, 0x1F, 0x00, 0xC0, 0xFC, 0x03, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ÿ
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x06, 0xFC, 0x0C, 0x00, 0x86, 0x3F, 0x0C, 0x00, 0xC6, 0x07, 0x0C, 0x00, 0xC6, 0x00, 0x0C, 0x00, 0xC6, 0x07, 0x0C, 0x00, 0x86, 0x3F, 0x0C, 0x00, 0x06, 0xFC, 0x0C, 0x00, 0x06, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ā
+ 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0xC0, 0x38, 0x87, 0x03, 0xC0, 0x1C, 0x07, 0x03, 0xC0, 0x0C, 0x03, 0x03, 0xC0, 0x0C, 0x03, 0x03, 0xC0, 0x0C, 0x03, 0x01, 0xC0, 0x0C, 0x83, 0x01, 0xC0, 0x1C, 0xE3, 0x00, 0xC0, 0xF8, 0xFF, 0x01, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ā
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x03, 0xF0, 0x0F, 0x00, 0x07, 0xFC, 0x0C, 0x00, 0x8E, 0x3F, 0x0C, 0x00, 0xCC, 0x07, 0x0C, 0x00, 0xCC, 0x00, 0x0C, 0x00, 0xCC, 0x07, 0x0C, 0x00, 0x8E, 0x3F, 0x0C, 0x00, 0x07, 0xFC, 0x0C, 0x00, 0x03, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ă
+ 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x30, 0x38, 0x87, 0x03, 0x70, 0x1C, 0x07, 0x03, 0xE0, 0x0C, 0x03, 0x03, 0xC0, 0x0C, 0x03, 0x03, 0xC0, 0x0C, 0x03, 0x01, 0xC0, 0x0C, 0x83, 0x01, 0xE0, 0x1C, 0xE3, 0x00, 0x70, 0xF8, 0xFF, 0x01, 0x30, 0xF8, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ă
+ 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x80, 0x3F, 0x0C, 0x00, 0xC0, 0x07, 0x0C, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0xC0, 0x07, 0x0C, 0x00, 0x80, 0x3F, 0x0C, 0x00, 0x00, 0xFC, 0x0C, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x80, 0x1F, 0x70, 0x00, 0x00, 0xFE, 0xFC, 0x00, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0xC0, 0xC3, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ą
+ 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x30, 0xFC, 0x01, 0x00, 0x38, 0xFE, 0x03, 0x00, 0x38, 0x87, 0x03, 0x00, 0x1C, 0x07, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x0C, 0x83, 0x01, 0x00, 0x1C, 0xE3, 0x00, 0x00, 0xF8, 0xFF, 0x71, 0x00, 0xF8, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ą
+ 0x12, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC8, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC4, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC2, 0x01, 0x80, 0x01, 0x82, 0x01, 0x80, 0x01, 0x80, 0x07, 0xC0, 0x01, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ć
+ 0x0C, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0x80, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0x60, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x80, 0x03, 0x30, 0x1C, 0x80, 0x03, 0x10, 0x78, 0xE0, 0x01, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ć
+ 0x12, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC8, 0x01, 0x80, 0x03, 0xCC, 0x00, 0x80, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xCC, 0x01, 0x80, 0x01, 0x88, 0x01, 0x80, 0x01, 0x80, 0x07, 0xC0, 0x01, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĉ
+ 0x0C, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x80, 0xF8, 0xFF, 0x01, 0xC0, 0x38, 0xC0, 0x01, 0x60, 0x1C, 0x80, 0x03, 0x70, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x70, 0x0C, 0x00, 0x03, 0x60, 0x0C, 0x80, 0x03, 0xC0, 0x1C, 0x80, 0x03, 0x80, 0x78, 0xE0, 0x01, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĉ
+ 0x12, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xC0, 0x01, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ċ
+ 0x0C, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x80, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ċ
+ 0x12, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0xC1, 0x01, 0x80, 0x03, 0xC3, 0x00, 0x80, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC3, 0x01, 0x80, 0x01, 0x81, 0x01, 0x80, 0x01, 0x80, 0x07, 0xC0, 0x01, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Č
+ 0x0C, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x10, 0xF8, 0xFF, 0x01, 0x30, 0x38, 0xC0, 0x01, 0x60, 0x1C, 0x80, 0x03, 0xE0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xE0, 0x0C, 0x00, 0x03, 0x60, 0x0C, 0x80, 0x03, 0x30, 0x1C, 0x80, 0x03, 0x10, 0x78, 0xE0, 0x01, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char č
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC1, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xC6, 0x01, 0x80, 0x03, 0x83, 0x01, 0x80, 0x01, 0x81, 0x03, 0xC0, 0x01, 0x80, 0x07, 0xE0, 0x01, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ď
+ 0x10, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x18, 0x80, 0x01, 0x00, 0x30, 0xE0, 0x00, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ď
+ 0x13, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x07, 0xE0, 0x01, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Đ
+ 0x0E, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x80, 0x0D, 0x00, 0x03, 0x80, 0x0D, 0x00, 0x03, 0x80, 0x19, 0x80, 0x01, 0x80, 0x31, 0xE0, 0x00, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char đ
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ē
+ 0x0D, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0xC0, 0x78, 0xC6, 0x01, 0xC0, 0x1C, 0x86, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x1C, 0x86, 0x03, 0xC0, 0x38, 0x86, 0x03, 0x00, 0xF8, 0xC7, 0x01, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0x80, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ē
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC7, 0x80, 0x01, 0x03, 0xCE, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCE, 0x80, 0x01, 0x03, 0xC7, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĕ
+ 0x0D, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x30, 0x78, 0xC6, 0x01, 0x70, 0x1C, 0x86, 0x03, 0xE0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xE0, 0x1C, 0x86, 0x03, 0x70, 0x38, 0x86, 0x03, 0x30, 0xF8, 0xC7, 0x01, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0x80, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĕ
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ė
+ 0x0D, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x78, 0xC6, 0x01, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x38, 0x86, 0x03, 0x00, 0xF8, 0xC7, 0x01, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0x80, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ė
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x73, 0xC0, 0x80, 0x01, 0xFF, 0xC0, 0x80, 0x01, 0xC7, 0xC0, 0x80, 0x01, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ę
+ 0x0D, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x78, 0xC6, 0x01, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x0C, 0x06, 0x73, 0x00, 0x0C, 0x06, 0xFF, 0x00, 0x0C, 0x06, 0xC7, 0x00, 0x0C, 0x06, 0xC3, 0x00, 0x1C, 0x86, 0xC3, 0x00, 0x38, 0x86, 0x03, 0x00, 0xF8, 0xC7, 0x01, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0x80, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ę
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC1, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xCE, 0x80, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCE, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC1, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ě
+ 0x0D, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x10, 0x78, 0xC6, 0x01, 0x30, 0x1C, 0x86, 0x03, 0x60, 0x0C, 0x06, 0x03, 0xE0, 0x0C, 0x06, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xE0, 0x0C, 0x06, 0x03, 0x60, 0x1C, 0x86, 0x03, 0x30, 0x38, 0x86, 0x03, 0x10, 0xF8, 0xC7, 0x01, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0x80, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ě
+ 0x13, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x88, 0x01, 0x80, 0x01, 0xCC, 0x00, 0x80, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x03, 0x03, 0xC6, 0x00, 0x03, 0x03, 0xCC, 0x00, 0x03, 0x03, 0xC8, 0x01, 0x83, 0x03, 0x80, 0x01, 0x83, 0x01, 0x80, 0x07, 0xC3, 0x01, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x06, 0xFF, 0x00, 0x00, 0x04, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĝ
+ 0x0E, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x10, 0x00, 0xF8, 0xFF, 0x71, 0x80, 0x3C, 0xC0, 0x73, 0xC0, 0x1C, 0x80, 0xE3, 0x60, 0x0C, 0x00, 0xC3, 0x70, 0x0C, 0x00, 0xC3, 0x30, 0x0C, 0x00, 0xC3, 0x70, 0x08, 0x00, 0xC1, 0x60, 0x18, 0x80, 0xE1, 0xC0, 0x70, 0xE0, 0x70, 0x80, 0xFC, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĝ
+ 0x13, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x83, 0x01, 0x80, 0x01, 0xC7, 0x00, 0x80, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x03, 0x03, 0xCE, 0x00, 0x03, 0x03, 0xC7, 0x00, 0x03, 0x03, 0xC3, 0x01, 0x83, 0x03, 0x80, 0x01, 0x83, 0x01, 0x80, 0x07, 0xC3, 0x01, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x06, 0xFF, 0x00, 0x00, 0x04, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ğ
+ 0x0E, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x10, 0x00, 0xF8, 0xFF, 0x71, 0x30, 0x3C, 0xC0, 0x73, 0x70, 0x1C, 0x80, 0xE3, 0xE0, 0x0C, 0x00, 0xC3, 0xC0, 0x0C, 0x00, 0xC3, 0xC0, 0x0C, 0x00, 0xC3, 0xC0, 0x08, 0x00, 0xC1, 0xE0, 0x18, 0x80, 0xE1, 0x70, 0x70, 0xE0, 0x70, 0x30, 0xFC, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ğ
+ 0x13, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x03, 0x03, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x01, 0x83, 0x03, 0x80, 0x01, 0x83, 0x01, 0x80, 0x07, 0xC3, 0x01, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x06, 0xFF, 0x00, 0x00, 0x04, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ġ
+ 0x0E, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x10, 0x00, 0xF8, 0xFF, 0x71, 0x00, 0x3C, 0xC0, 0x73, 0x00, 0x1C, 0x80, 0xE3, 0x00, 0x0C, 0x00, 0xC3, 0xC0, 0x0C, 0x00, 0xC3, 0xC0, 0x0C, 0x00, 0xC3, 0xC0, 0x08, 0x00, 0xC1, 0x00, 0x18, 0x80, 0xE1, 0x00, 0x70, 0xE0, 0x70, 0x00, 0xFC, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ġ
+ 0x13, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x9B, 0xC0, 0x00, 0x00, 0xFB, 0xC0, 0x00, 0x03, 0x7B, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x00, 0x03, 0x03, 0xC0, 0x01, 0x83, 0x03, 0x80, 0x01, 0x83, 0x01, 0x80, 0x07, 0xC3, 0x01, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x06, 0xFF, 0x00, 0x00, 0x04, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ģ
+ 0x0E, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x10, 0x00, 0xF8, 0xFF, 0x71, 0x00, 0x3C, 0xC0, 0x73, 0x00, 0x1C, 0x80, 0xE3, 0x00, 0x0C, 0x00, 0xC3, 0xF0, 0x0C, 0x00, 0xC3, 0xF8, 0x0C, 0x00, 0xC3, 0xC8, 0x08, 0x00, 0xC1, 0x00, 0x18, 0x80, 0xE1, 0x00, 0x70, 0xE0, 0x70, 0x00, 0xFC, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x3F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ģ
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x08, 0xC0, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x08, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĥ
+ 0x0D, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE4, 0xFF, 0xFF, 0x03, 0x06, 0x30, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x06, 0x1C, 0x00, 0x00, 0x04, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĥ
+ 0x14, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ħ
+ 0x0F, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x80, 0x31, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ħ
+ 0x08, 0x0C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xC3, 0xFF, 0xFF, 0x03, 0xC6, 0xFF, 0xFF, 0x03, 0xC6, 0xFF, 0xFF, 0x03, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĩ
+ 0x08, 0xC0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĩ
+ 0x06, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC6, 0xFF, 0xFF, 0x03, 0xC6, 0xFF, 0xFF, 0x03, 0xC6, 0xFF, 0xFF, 0x03, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ī
+ 0x06, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xFC, 0xFF, 0x03, 0xC0, 0xFC, 0xFF, 0x03, 0xC0, 0xFC, 0xFF, 0x03, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ī
+ 0x08, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xCC, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĭ
+ 0x08, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0xFC, 0xFF, 0x03, 0xC0, 0xFC, 0xFF, 0x03, 0xC0, 0xFC, 0xFF, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĭ
+ 0x05, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xFF, 0xFF, 0xC7, 0xC0, 0xFF, 0xFF, 0xC3, 0xC0, 0xFF, 0xFF, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Į
+ 0x05, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFC, 0x60, 0xFC, 0xFF, 0xC7, 0x60, 0xFC, 0xFF, 0xC3, 0x60, 0xFC, 0xFF, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char į
+ 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char İ
+ 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ı
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0xC0, 0x01, 0xC0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char IJ
+ 0x09, 0x60, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x60, 0xFC, 0xFF, 0xFF, 0x60, 0xFC, 0xFF, 0x7F, 0x60, 0xFC, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ij
+ 0x0D, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC7, 0x00, 0x80, 0x03, 0xC3, 0x00, 0xC0, 0x01, 0xC7, 0xFF, 0xFF, 0x01, 0xC6, 0xFF, 0xFF, 0x00, 0xCC, 0xFF, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĵ
+ 0x07, 0x80, 0x01, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xE0, 0xFC, 0xFF, 0xFF, 0x60, 0xFC, 0xFF, 0x7F, 0xE0, 0xFC, 0xFF, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĵ
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF0, 0x07, 0x98, 0x00, 0x38, 0x0F, 0xF8, 0x00, 0x1C, 0x1E, 0x78, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x80, 0x03, 0xE0, 0x01, 0xC0, 0x01, 0xC0, 0x03, 0xC0, 0x00, 0x80, 0x03, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ķ
+ 0x0D, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x0F, 0x98, 0x00, 0xC0, 0x1F, 0xF8, 0x00, 0xE0, 0x3C, 0x78, 0x00, 0x70, 0xF0, 0x00, 0x00, 0x38, 0xE0, 0x01, 0x00, 0x1C, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ķ
+ 0x0D, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3C, 0x00, 0x00, 0x70, 0xF0, 0x00, 0x00, 0x38, 0xE0, 0x01, 0x00, 0x1C, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĸ
+ 0x0D, 0xC0, 0xFF, 0xFF, 0x03, 0xC8, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0x04, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ĺ
+ 0x04, 0xE4, 0xFF, 0xFF, 0x03, 0xE6, 0xFF, 0xFF, 0x03, 0xE2, 0xFF, 0xFF, 0x03, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ĺ
+ 0x0D, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ļ
+ 0x03, 0xE0, 0xFF, 0xFF, 0x9B, 0xE0, 0xFF, 0xFF, 0xFB, 0xE0, 0xFF, 0xFF, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ļ
+ 0x0D, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x04, 0x00, 0x03, 0xC0, 0x07, 0x00, 0x03, 0xC0, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ľ
+ 0x06, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ľ
+ 0x0D, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŀ
+ 0x07, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŀ
+ 0x0F, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0x03, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ł
+ 0x05, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ł
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x08, 0xF0, 0x01, 0x00, 0x0C, 0xC0, 0x03, 0x00, 0x04, 0x80, 0x0F, 0x00, 0x06, 0x00, 0x3E, 0x00, 0x02, 0x00, 0x78, 0x00, 0x02, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ń
+ 0x0D, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x80, 0x0C, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x30, 0x1C, 0x00, 0x00, 0x10, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ń
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x98, 0x00, 0xC0, 0x03, 0xF8, 0x00, 0x80, 0x0F, 0x78, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ņ
+ 0x0D, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x98, 0x00, 0x0C, 0x00, 0xF8, 0x00, 0x0C, 0x00, 0x78, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ņ
+ 0x11, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, 0x03, 0x1E, 0x00, 0x00, 0x06, 0x78, 0x00, 0x00, 0x0E, 0xF0, 0x01, 0x00, 0x0C, 0xC0, 0x03, 0x00, 0x0E, 0x80, 0x0F, 0x00, 0x06, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x78, 0x00, 0x01, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ň
+ 0x0D, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x10, 0xFC, 0xFF, 0x03, 0x30, 0x30, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x30, 0x1C, 0x00, 0x00, 0x10, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ň
+ 0x10, 0xC0, 0x11, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ʼn
+ 0x10, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0xC0, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0x80, 0x03, 0xC0, 0x03, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŋ
+ 0x0D, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0xC0, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŋ
+ 0x13, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x01, 0xC0, 0x01, 0xC6, 0x01, 0x80, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xE0, 0x01, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ō
+ 0x0D, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0xC0, 0x38, 0xE0, 0x01, 0xC0, 0x1C, 0x80, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x1C, 0x80, 0x03, 0xC0, 0x38, 0xC0, 0x01, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ō
+ 0x13, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x01, 0xC0, 0x01, 0xC3, 0x01, 0x80, 0x03, 0xC7, 0x00, 0x00, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xCE, 0x00, 0x00, 0x03, 0xC7, 0x01, 0x80, 0x01, 0x83, 0x01, 0x80, 0x01, 0x80, 0x07, 0xE0, 0x01, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŏ
+ 0x0D, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x30, 0x38, 0xE0, 0x01, 0x70, 0x1C, 0x80, 0x03, 0xE0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xE0, 0x1C, 0x80, 0x03, 0x70, 0x38, 0xC0, 0x01, 0x30, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŏ
+ 0x13, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x80, 0x07, 0xE0, 0x01, 0x80, 0x01, 0xC0, 0x01, 0xC8, 0x01, 0x80, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x03, 0xC9, 0x00, 0x00, 0x03, 0xCC, 0x00, 0x00, 0x03, 0xC6, 0x01, 0x80, 0x01, 0x83, 0x01, 0x80, 0x01, 0x83, 0x07, 0xE0, 0x01, 0x01, 0x1F, 0xF8, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ő
+ 0x0D, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x80, 0x38, 0xE0, 0x01, 0xC0, 0x1C, 0x80, 0x03, 0x60, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x00, 0x03, 0x90, 0x0C, 0x00, 0x03, 0xC0, 0x1C, 0x80, 0x03, 0x60, 0x38, 0xC0, 0x01, 0x30, 0xF8, 0xFF, 0x01, 0x30, 0xF0, 0xFF, 0x00, 0x10, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ő
+ 0x1B, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x80, 0x03, 0xE0, 0x01, 0x80, 0x01, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0xC0, 0x80, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, // Code for char Œ
+ 0x18, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x38, 0xC0, 0x01, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x78, 0xC6, 0x01, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x0C, 0x06, 0x03, 0x00, 0x1C, 0x86, 0x03, 0x00, 0x38, 0x86, 0x03, 0x00, 0xF8, 0xC7, 0x01, 0x00, 0xF0, 0xC7, 0x00, 0x00, 0x80, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char œ
+ 0x12, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC8, 0x00, 0x03, 0x00, 0xCC, 0x00, 0x03, 0x00, 0xC4, 0x00, 0x03, 0x00, 0xC6, 0x00, 0x03, 0x00, 0xC2, 0x00, 0x0F, 0x00, 0xC2, 0x00, 0x1F, 0x00, 0xC0, 0x81, 0x7F, 0x00, 0x80, 0xC3, 0xF1, 0x00, 0x80, 0xFF, 0xE1, 0x01, 0x00, 0xFF, 0x80, 0x03, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŕ
+ 0x07, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x80, 0xFC, 0xFF, 0x03, 0xC0, 0x30, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŕ
+ 0x12, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x03, 0x98, 0xC0, 0x00, 0x03, 0xF8, 0xC0, 0x00, 0x03, 0x78, 0xC0, 0x00, 0x0F, 0x00, 0xC0, 0x00, 0x1F, 0x00, 0xC0, 0x81, 0x7F, 0x00, 0x80, 0xC3, 0xF1, 0x00, 0x80, 0xFF, 0xE1, 0x01, 0x00, 0xFF, 0x80, 0x03, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŗ
+ 0x06, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xFC, 0xFF, 0xFB, 0x00, 0xFC, 0xFF, 0x7B, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŗ
+ 0x12, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x00, 0x03, 0x00, 0xC1, 0x00, 0x03, 0x00, 0xC3, 0x00, 0x03, 0x00, 0xC6, 0x00, 0x03, 0x00, 0xCE, 0x00, 0x03, 0x00, 0xCC, 0x00, 0x03, 0x00, 0xCE, 0x00, 0x03, 0x00, 0xC6, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x1F, 0x00, 0xC1, 0x81, 0x7F, 0x00, 0x80, 0xC3, 0xF1, 0x00, 0x80, 0xFF, 0xE1, 0x01, 0x00, 0xFF, 0x80, 0x03, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ř
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x30, 0xFC, 0xFF, 0x03, 0x60, 0xFC, 0xFF, 0x03, 0xE0, 0xFC, 0xFF, 0x03, 0xC0, 0x30, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ř
+ 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1E, 0xE0, 0x00, 0x00, 0x7F, 0xF0, 0x01, 0x80, 0x7F, 0xC0, 0x01, 0x80, 0xE1, 0x80, 0x01, 0xC0, 0xE1, 0x80, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC8, 0xC0, 0x01, 0x03, 0xCC, 0xC0, 0x01, 0x03, 0xC6, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC3, 0x80, 0x03, 0x03, 0xC1, 0x81, 0x83, 0x03, 0x80, 0x03, 0xC7, 0x01, 0x80, 0x07, 0xFF, 0x01, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x06, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ś
+ 0x0D, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF0, 0xC0, 0x01, 0x00, 0xF8, 0xC1, 0x01, 0x00, 0xF8, 0x83, 0x03, 0x00, 0x9C, 0x83, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x80, 0x0C, 0x07, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0x60, 0x0C, 0x06, 0x03, 0x30, 0x0C, 0x0E, 0x03, 0x30, 0x1C, 0x8E, 0x03, 0x10, 0x38, 0xFC, 0x01, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x20, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ś
+ 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1E, 0xE0, 0x00, 0x00, 0x7F, 0xF0, 0x01, 0x80, 0x7F, 0xC0, 0x01, 0x80, 0xE1, 0x80, 0x01, 0xC8, 0xE1, 0x80, 0x03, 0xCC, 0xC0, 0x00, 0x03, 0xC6, 0xC0, 0x01, 0x03, 0xC7, 0xC0, 0x01, 0x03, 0xC3, 0x80, 0x01, 0x03, 0xC7, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x03, 0x03, 0xCC, 0x81, 0x83, 0x03, 0x88, 0x03, 0xC7, 0x01, 0x80, 0x07, 0xFF, 0x01, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x06, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŝ
+ 0x0D, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF0, 0xC0, 0x01, 0x00, 0xF8, 0xC1, 0x01, 0x80, 0xF8, 0x83, 0x03, 0xC0, 0x9C, 0x83, 0x03, 0x60, 0x0C, 0x03, 0x03, 0x70, 0x0C, 0x07, 0x03, 0x30, 0x0C, 0x06, 0x03, 0x70, 0x0C, 0x06, 0x03, 0x60, 0x0C, 0x0E, 0x03, 0xC0, 0x1C, 0x8E, 0x03, 0x80, 0x38, 0xFC, 0x01, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x20, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŝ
+ 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1E, 0xE0, 0x00, 0x00, 0x7F, 0xF0, 0x01, 0x80, 0x7F, 0xC0, 0x01, 0x80, 0xE1, 0x80, 0x01, 0xC0, 0xE1, 0x80, 0x03, 0xC0, 0xC0, 0x00, 0x03, 0xC0, 0xC0, 0x01, 0xC3, 0xC0, 0xC0, 0x01, 0xCB, 0xC0, 0x80, 0x01, 0xCF, 0xC0, 0x80, 0x01, 0xFB, 0xC0, 0x80, 0x03, 0x73, 0xC0, 0x81, 0x83, 0x03, 0x80, 0x03, 0xC7, 0x01, 0x80, 0x07, 0xFF, 0x01, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x06, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ş
+ 0x0D, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF0, 0xC0, 0x01, 0x00, 0xF8, 0xC1, 0x01, 0x00, 0xF8, 0x83, 0x03, 0x00, 0x9C, 0x83, 0x03, 0x00, 0x0C, 0x03, 0xC3, 0x00, 0x0C, 0x07, 0xCB, 0x00, 0x0C, 0x06, 0xCF, 0x00, 0x0C, 0x06, 0xFB, 0x00, 0x0C, 0x0E, 0x73, 0x00, 0x1C, 0x8E, 0x03, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x20, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ş
+ 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1E, 0xE0, 0x00, 0x00, 0x7F, 0xF0, 0x01, 0x80, 0x7F, 0xC0, 0x01, 0x80, 0xE1, 0x80, 0x01, 0xC1, 0xE1, 0x80, 0x03, 0xC3, 0xC0, 0x00, 0x03, 0xC6, 0xC0, 0x01, 0x03, 0xCE, 0xC0, 0x01, 0x03, 0xCC, 0x80, 0x01, 0x03, 0xCE, 0x80, 0x01, 0x03, 0xC6, 0x80, 0x03, 0x03, 0xC3, 0x81, 0x83, 0x03, 0x81, 0x03, 0xC7, 0x01, 0x80, 0x07, 0xFF, 0x01, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x06, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Š
+ 0x0E, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF0, 0xC0, 0x01, 0x00, 0xF8, 0xC1, 0x01, 0x10, 0xF8, 0x83, 0x03, 0x30, 0x9C, 0x83, 0x03, 0x60, 0x0C, 0x03, 0x03, 0xE0, 0x0C, 0x07, 0x03, 0xC0, 0x0C, 0x06, 0x03, 0xE0, 0x0C, 0x06, 0x03, 0x60, 0x0C, 0x0E, 0x03, 0x30, 0x1C, 0x8E, 0x03, 0x10, 0x38, 0xFC, 0x01, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x20, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char š
+ 0x10, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x9B, 0xC0, 0xFF, 0xFF, 0xFB, 0xC0, 0xFF, 0xFF, 0x7B, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ţ
+ 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x9B, 0x00, 0x0C, 0x00, 0xFB, 0x00, 0x0C, 0x00, 0x7B, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ţ
+ 0x10, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xCE, 0xFF, 0xFF, 0x03, 0xCC, 0xFF, 0xFF, 0x03, 0xCE, 0xFF, 0xFF, 0x03, 0xC6, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ť
+ 0x0A, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x60, 0x02, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ť
+ 0x10, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x80, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŧ
+ 0x08, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x0C, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŧ
+ 0x10, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x0C, 0x00, 0x80, 0x01, 0x07, 0x00, 0x80, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x80, 0x01, 0x03, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ũ
+ 0x0D, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0xC0, 0xFC, 0xFF, 0x03, 0x70, 0x00, 0x80, 0x03, 0x30, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0xC0, 0x00, 0xE0, 0xFC, 0xFF, 0x03, 0x30, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ũ
+ 0x10, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x06, 0x00, 0x80, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x80, 0x03, 0x06, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ū
+ 0x0D, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0xFC, 0xFF, 0x03, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ū
+ 0x10, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x03, 0x00, 0x80, 0x01, 0x07, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x0E, 0x00, 0x00, 0x03, 0x07, 0x00, 0x80, 0x03, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŭ
+ 0x0D, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x30, 0xFC, 0xFF, 0x03, 0x70, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x80, 0x01, 0x70, 0x00, 0xC0, 0x00, 0x30, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŭ
+ 0x10, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x0E, 0x00, 0x80, 0x03, 0x1F, 0x00, 0x00, 0x03, 0x31, 0x00, 0x00, 0x03, 0x31, 0x00, 0x00, 0x03, 0x31, 0x00, 0x00, 0x03, 0x1F, 0x00, 0x00, 0x03, 0x0E, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ů
+ 0x0D, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0xFC, 0xFF, 0x03, 0x38, 0x00, 0x80, 0x03, 0x7C, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0xC6, 0x00, 0x00, 0x03, 0x7C, 0x00, 0x80, 0x01, 0x38, 0x00, 0xC0, 0x00, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ů
+ 0x10, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x08, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x80, 0x03, 0x06, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x03, 0x06, 0x00, 0x80, 0x03, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0xE0, 0x01, 0xC1, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ű
+ 0x0D, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0xFC, 0xFF, 0x03, 0x80, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x90, 0x00, 0x80, 0x01, 0xC0, 0x00, 0xC0, 0x00, 0x60, 0xFC, 0xFF, 0x03, 0x30, 0xFC, 0xFF, 0x03, 0x30, 0xFC, 0xFF, 0x03, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ű
+ 0x10, 0xC0, 0xFF, 0x1F, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x80, 0xC3, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0xC0, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ų
+ 0x0D, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x80, 0xFD, 0x00, 0x00, 0xC0, 0xC4, 0x00, 0xFC, 0xFF, 0xC3, 0x00, 0xFC, 0xFF, 0xC3, 0x00, 0xFC, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ų
+ 0x1A, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0xFF, 0x00, 0x0C, 0xF0, 0x1F, 0x00, 0x06, 0xFE, 0x01, 0x00, 0xC7, 0x1F, 0x00, 0x00, 0xC3, 0x01, 0x00, 0x00, 0xC7, 0x1F, 0x00, 0x00, 0x06, 0xFE, 0x01, 0x00, 0x0C, 0xF0, 0x1F, 0x00, 0x08, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0xFF, 0x01, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0xFF, 0x03, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŵ
+ 0x14, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0xF0, 0x03, 0xC0, 0x00, 0xFF, 0x00, 0x60, 0xF0, 0x0F, 0x00, 0x70, 0xFC, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x70, 0xFC, 0x00, 0x00, 0x60, 0xF0, 0x0F, 0x00, 0xC0, 0x00, 0xFF, 0x00, 0x80, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x80, 0xFF, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŵ
+ 0x12, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x08, 0x3C, 0x00, 0x00, 0x0C, 0xF8, 0x00, 0x00, 0x06, 0xE0, 0x01, 0x00, 0x07, 0xC0, 0xFF, 0x03, 0x03, 0x80, 0xFF, 0x03, 0x07, 0xC0, 0xFF, 0x03, 0x06, 0xE0, 0x01, 0x00, 0x0C, 0xF8, 0x00, 0x00, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ŷ
+ 0x0D, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0xFC, 0x01, 0xC0, 0x80, 0xF0, 0x0F, 0xC0, 0xC0, 0x80, 0x3F, 0xE0, 0x60, 0x00, 0xFC, 0x79, 0x70, 0x00, 0xC0, 0x3F, 0x30, 0x00, 0xC0, 0x0F, 0x70, 0x00, 0xF8, 0x01, 0x60, 0x00, 0x3F, 0x00, 0xC0, 0xE0, 0x0F, 0x00, 0x80, 0xFC, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ŷ
+ 0x12, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0E, 0xF8, 0x00, 0x00, 0x0E, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x80, 0xFF, 0x03, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x0E, 0xF8, 0x00, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ÿ
+ 0x10, 0x00, 0x00, 0x80, 0x03, 0xC0, 0x00, 0xC0, 0x03, 0xC0, 0x00, 0xE0, 0x03, 0xC0, 0x00, 0x78, 0x03, 0xC0, 0x00, 0x3C, 0x03, 0xC0, 0x00, 0x1E, 0x03, 0xC8, 0x80, 0x07, 0x03, 0xCC, 0xC0, 0x03, 0x03, 0xC4, 0xE0, 0x01, 0x03, 0xC6, 0x78, 0x00, 0x03, 0xC2, 0x3C, 0x00, 0x03, 0xC2, 0x1E, 0x00, 0x03, 0xC0, 0x07, 0x00, 0x03, 0xC0, 0x03, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ź
+ 0x0D, 0x00, 0x00, 0x80, 0x03, 0x00, 0x0C, 0xC0, 0x03, 0x00, 0x0C, 0xE0, 0x03, 0x00, 0x0C, 0x78, 0x03, 0x00, 0x0C, 0x3C, 0x03, 0x80, 0x0C, 0x1E, 0x03, 0xC0, 0x8C, 0x07, 0x03, 0x60, 0xCC, 0x03, 0x03, 0x30, 0xEC, 0x01, 0x03, 0x30, 0x7C, 0x00, 0x03, 0x10, 0x3C, 0x00, 0x03, 0x00, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ź
+ 0x10, 0x00, 0x00, 0x80, 0x03, 0xC0, 0x00, 0xC0, 0x03, 0xC0, 0x00, 0xE0, 0x03, 0xC0, 0x00, 0x78, 0x03, 0xC0, 0x00, 0x3C, 0x03, 0xC0, 0x00, 0x1E, 0x03, 0xC0, 0x80, 0x07, 0x03, 0xCC, 0xC0, 0x03, 0x03, 0xCC, 0xE0, 0x01, 0x03, 0xCC, 0x78, 0x00, 0x03, 0xC0, 0x3C, 0x00, 0x03, 0xC0, 0x1E, 0x00, 0x03, 0xC0, 0x07, 0x00, 0x03, 0xC0, 0x03, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ż
+ 0x0D, 0x00, 0x00, 0x80, 0x03, 0x00, 0x0C, 0xC0, 0x03, 0x00, 0x0C, 0xE0, 0x03, 0x00, 0x0C, 0x78, 0x03, 0x00, 0x0C, 0x3C, 0x03, 0xC0, 0x0C, 0x1E, 0x03, 0xC0, 0x8C, 0x07, 0x03, 0xC0, 0xCC, 0x03, 0x03, 0x00, 0xEC, 0x01, 0x03, 0x00, 0x7C, 0x00, 0x03, 0x00, 0x3C, 0x00, 0x03, 0x00, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ż
+ 0x10, 0x00, 0x00, 0x80, 0x03, 0xC0, 0x00, 0xC0, 0x03, 0xC0, 0x00, 0xE0, 0x03, 0xC0, 0x00, 0x78, 0x03, 0xC1, 0x00, 0x3C, 0x03, 0xC3, 0x00, 0x1E, 0x03, 0xC6, 0x80, 0x07, 0x03, 0xCE, 0xC0, 0x03, 0x03, 0xCC, 0xE0, 0x01, 0x03, 0xCE, 0x78, 0x00, 0x03, 0xC6, 0x3C, 0x00, 0x03, 0xC3, 0x1E, 0x00, 0x03, 0xC1, 0x07, 0x00, 0x03, 0xC0, 0x03, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ž
+ 0x0D, 0x00, 0x00, 0x80, 0x03, 0x00, 0x0C, 0xC0, 0x03, 0x10, 0x0C, 0xE0, 0x03, 0x30, 0x0C, 0x78, 0x03, 0x60, 0x0C, 0x3C, 0x03, 0xE0, 0x0C, 0x1E, 0x03, 0xC0, 0x8C, 0x07, 0x03, 0xE0, 0xCC, 0x03, 0x03, 0x60, 0xEC, 0x01, 0x03, 0x30, 0x7C, 0x00, 0x03, 0x10, 0x3C, 0x00, 0x03, 0x00, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ž
+ 0x04, 0x80, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0xE0, 0xFF, 0xFF, 0x03, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ſ
+
+#if USE_CYRILLIC_CHARACTERS
+ // Cyrillic characters 0x401 to 0x491
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x98, 0x81, 0x01, 0x03, 0x98, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x98, 0x81, 0x01, 0x03, 0x98, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x15, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x01, 0x80, 0x61, 0x80, 0x01, 0x80, 0x61, 0x80, 0x03, 0x80, 0x61, 0x00, 0x03, 0x00, 0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0x03, 0x00, 0xC0, 0x80, 0x03, 0x00, 0xC0, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x9C, 0x79, 0x00, 0x00, 0x86, 0xE1, 0x00, 0x00, 0x83, 0xC1, 0x01, 0x00, 0x83, 0x81, 0x01, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x03, 0x80, 0x03, 0x00, 0x03, 0x80, 0x01, 0x00, 0x0F, 0xC0, 0x01, 0x00, 0x1E, 0xF0, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x3C, 0xE0, 0x00, 0x00, 0x7E, 0xC0, 0x01, 0x00, 0xE3, 0x80, 0x01, 0x00, 0xC3, 0x80, 0x03, 0x80, 0xC1, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x03, 0x03, 0x80, 0x01, 0x03, 0x03, 0x80, 0x01, 0x03, 0x03, 0x00, 0x03, 0x83, 0x01, 0x00, 0x07, 0xC6, 0x01, 0x00, 0x0E, 0xFE, 0x00, 0x00, 0x0C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0B, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x1C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x83, 0x03, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, // Code for char
+ 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x83, 0x03, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x15, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x10, 0xC0, 0x01, 0x00, 0x18, 0xE0, 0x07, 0x00, 0x08, 0x70, 0x0F, 0x00, 0x0C, 0x38, 0x3C, 0x00, 0x04, 0x1E, 0x78, 0x00, 0x04, 0x07, 0xF0, 0x01, 0x80, 0x03, 0xC0, 0x03, 0x80, 0x01, 0x80, 0x03, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x03, 0x80, 0x03, 0x80, 0x07, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x03, 0x06, 0x7C, 0x00, 0x03, 0x0E, 0xF0, 0x80, 0x03, 0x1C, 0xE0, 0xC3, 0x01, 0x18, 0x80, 0xF7, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x1E, 0x00, 0x18, 0x80, 0x07, 0x00, 0x1C, 0xE0, 0x01, 0x00, 0x0E, 0xF8, 0x00, 0x00, 0x06, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x7C, 0x0C, 0x00, 0x00, 0x1F, 0x0C, 0x00, 0x80, 0x03, 0x0C, 0x00, 0x80, 0x03, 0x0C, 0x00, 0x00, 0x1F, 0x0C, 0x00, 0x00, 0x7C, 0x0C, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x83, 0x03, 0x80, 0x01, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0xC3, 0x01, 0x03, 0x00, 0x43, 0x83, 0x01, 0x00, 0x7F, 0xC6, 0x01, 0x00, 0x3E, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xFE, 0x03, 0x00, 0xFC, 0x3F, 0x03, 0x80, 0xFF, 0x01, 0x03, 0x80, 0x03, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x19, 0x80, 0x00, 0x00, 0x02, 0x80, 0x01, 0x80, 0x03, 0x80, 0x03, 0xC0, 0x03, 0x00, 0x0F, 0xF0, 0x01, 0x00, 0x1E, 0x7C, 0x00, 0x00, 0x38, 0x1E, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x70, 0x0F, 0x00, 0x00, 0x3C, 0x3C, 0x00, 0x00, 0x0E, 0x78, 0x00, 0x00, 0x07, 0xE0, 0x01, 0x80, 0x03, 0xC0, 0x03, 0x80, 0x01, 0x80, 0x03, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x0F, 0xE0, 0x01, 0x00, 0x07, 0x80, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0xC3, 0x83, 0x01, 0x00, 0xC3, 0xC3, 0x01, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x3C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x06, 0x00, 0xE0, 0x01, 0x0E, 0x00, 0x78, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x18, 0x00, 0x0F, 0x00, 0x18, 0xC0, 0x07, 0x00, 0x18, 0xE0, 0x01, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x0E, 0x0F, 0x00, 0x00, 0x86, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x70, 0x0F, 0x00, 0x00, 0x38, 0x3C, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x07, 0xF0, 0x01, 0x80, 0x03, 0xC0, 0x03, 0x80, 0x01, 0x80, 0x03, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xE0, 0x7F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x06, 0xE0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x00, 0x83, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x1C, 0x78, 0x00, 0x00, 0x06, 0xE0, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x00, 0x03, 0x80, 0x01, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x00, 0x03, 0x80, 0x01, 0x00, 0x0F, 0xC0, 0x01, 0x00, 0x1E, 0xF0, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char !
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char "
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x03, 0x80, 0x03, 0x80, 0x07, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x03, 0x00, 0x7C, 0x00, 0x03, 0x00, 0xF0, 0x80, 0x03, 0x00, 0xE0, 0xC3, 0x01, 0x00, 0x80, 0xF7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char #
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x0E, 0x70, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x0E, 0x70, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02, 0x80, 0x01, 0x80, 0x03, 0x80, 0x03, 0xC0, 0x03, 0x00, 0x0F, 0xE0, 0x01, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x78, 0x3C, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x78, 0x3E, 0x00, 0x00, 0x1E, 0x78, 0x00, 0x00, 0x0F, 0xF0, 0x01, 0x80, 0x03, 0xC0, 0x03, 0x80, 0x01, 0x80, 0x03, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char %
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char &
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char '
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char (
+ 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char )
+ 0x14, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x83, 0x03, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char *
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x83, 0x03, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char +
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x83, 0x03, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ,
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x1E, 0xE0, 0x00, 0x00, 0x07, 0xC0, 0x01, 0x00, 0x03, 0x80, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x80, 0x81, 0x01, 0x03, 0x00, 0x83, 0x81, 0x01, 0x00, 0x83, 0x81, 0x01, 0x00, 0x86, 0xE1, 0x00, 0x00, 0x9E, 0xF9, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char -
+ 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x1E, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x01, 0x00, 0x03, 0x80, 0x01, 0x80, 0x03, 0x80, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x01, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x00, 0x03, 0x80, 0x01, 0x00, 0x07, 0xC0, 0x01, 0x00, 0x1E, 0xF0, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char .
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x7C, 0x00, 0x03, 0x00, 0xFE, 0xC0, 0x03, 0x00, 0xC7, 0xE1, 0x00, 0x80, 0x83, 0x79, 0x00, 0x80, 0x01, 0x1F, 0x00, 0x80, 0x01, 0x0F, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0x01, 0x03, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char /
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xF8, 0x00, 0x00, 0x70, 0xFC, 0x01, 0x00, 0x30, 0x8C, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x86, 0x01, 0x00, 0x38, 0xC6, 0x00, 0x00, 0xF0, 0xFF, 0x01, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0xC7, 0xC1, 0x01, 0x00, 0x63, 0x00, 0x03, 0x80, 0x31, 0x00, 0x03, 0x80, 0x31, 0x00, 0x03, 0xC0, 0x31, 0x00, 0x03, 0xC0, 0x30, 0x00, 0x03, 0xC0, 0x30, 0x00, 0x03, 0xC0, 0x70, 0x80, 0x03, 0xC0, 0xE0, 0xFF, 0x01, 0xC0, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x38, 0x8F, 0x03, 0x00, 0xF0, 0xFD, 0x01, 0x00, 0xE0, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3
+ 0x0F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF8, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xF8, 0x1F, 0x03, 0x00, 0xF8, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xC6, 0x01, 0x00, 0x38, 0x86, 0x01, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x38, 0x06, 0x03, 0x00, 0x70, 0x86, 0x01, 0x00, 0xE0, 0xC7, 0x01, 0x00, 0x80, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5
+ 0x12, 0x00, 0x08, 0x00, 0x02, 0x00, 0x18, 0x80, 0x03, 0x00, 0x78, 0xE0, 0x03, 0x00, 0xE0, 0xF8, 0x00, 0x00, 0xC0, 0x3D, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x3D, 0x00, 0x00, 0xE0, 0xF8, 0x00, 0x00, 0x78, 0xE0, 0x03, 0x00, 0x18, 0x80, 0x03, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x38, 0x80, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x38, 0x8F, 0x03, 0x00, 0xF0, 0xF9, 0x01, 0x00, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x60, 0xF8, 0xFF, 0x03, 0xE0, 0x00, 0xC0, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0x80, 0x01, 0x78, 0x00, 0x80, 0x01, 0x1E, 0x00, 0x80, 0x01, 0x07, 0x00, 0x80, 0xC1, 0x03, 0x00, 0xC0, 0xE1, 0x00, 0x00, 0xE0, 0x78, 0x00, 0x00, 0x60, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x3D, 0x00, 0x00, 0xE0, 0xF0, 0x00, 0x00, 0x78, 0xE0, 0x03, 0x00, 0x18, 0x80, 0x03, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char :
+ 0x0E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xFC, 0x01, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ;
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char <
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char =
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x38, 0x80, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x38, 0x80, 0x03, 0x00, 0x70, 0xC0, 0x01, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char >
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ?
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x00, 0xF8, 0xFF, 0xFF, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x30, 0x80, 0x01, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x38, 0x80, 0x03, 0x00, 0x70, 0xC0, 0x01, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char @
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x30, 0x80, 0x01, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x38, 0x80, 0x03, 0x00, 0x70, 0xC0, 0x01, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A
+ 0x0C, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B
+ 0x0E, 0x00, 0x18, 0x00, 0x00, 0x00, 0x78, 0x00, 0xC0, 0x00, 0xF8, 0x03, 0xC0, 0x00, 0xC0, 0x0F, 0xC0, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0xF8, 0x79, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C
+ 0x16, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x38, 0x80, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x30, 0x80, 0x01, 0x00, 0x60, 0xC0, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x30, 0x80, 0x01, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x38, 0x80, 0x03, 0x00, 0x70, 0xC0, 0x01, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D
+ 0x0D, 0x00, 0x08, 0x00, 0x02, 0x00, 0x18, 0x80, 0x03, 0x00, 0x78, 0xC0, 0x03, 0x00, 0xF0, 0xE0, 0x00, 0x00, 0xC0, 0x7B, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xC0, 0x7B, 0x00, 0x00, 0xF0, 0xE0, 0x00, 0x00, 0x78, 0xC0, 0x03, 0x00, 0x18, 0x80, 0x03, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I
+ 0x0F, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8E, 0x01, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x30, 0x80, 0x01, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x38, 0x86, 0x03, 0x00, 0x70, 0xC6, 0x01, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x38, 0x80, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x38, 0x80, 0x03, 0x00, 0x70, 0xC0, 0x01, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N
+ 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xE0, 0x03, 0x03, 0x00, 0xF0, 0x87, 0x03, 0x00, 0x30, 0xE6, 0x01, 0x00, 0x18, 0x7C, 0x00, 0x00, 0x18, 0x3C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xC6, 0x01, 0xC0, 0x38, 0x86, 0x01, 0xC0, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0xC0, 0x38, 0x06, 0x03, 0xC0, 0x70, 0x86, 0x01, 0x00, 0xE0, 0xC7, 0x01, 0x00, 0x80, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q
+ 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x63, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0xF0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char R
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x19, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0xC0, 0x18, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x20, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S
+ 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x70, 0xE6, 0x01, 0x00, 0x30, 0x86, 0x01, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x18, 0x00, 0x03, 0x00, 0x30, 0x80, 0x01, 0x00, 0x70, 0xC0, 0x01, 0x00, 0x40, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC1, 0x00, 0x00, 0xF0, 0xC3, 0x01, 0x00, 0x38, 0x83, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x06, 0x03, 0x00, 0x18, 0x0E, 0x03, 0x00, 0x18, 0x0C, 0x03, 0x00, 0x30, 0x9C, 0x01, 0x00, 0x70, 0xF8, 0x01, 0x00, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFF, 0x03, 0xC0, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char W
+ 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xF8, 0xFF, 0xFF, 0xC0, 0xF8, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X
+ 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xFC, 0x01, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8E, 0x01, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z
+ 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x63, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x07, 0x00, 0x80, 0x81, 0x0F, 0x00, 0xC0, 0xC0, 0x3D, 0x00, 0x60, 0xE0, 0xF0, 0x00, 0x60, 0x78, 0xE0, 0x03, 0x20, 0x18, 0x80, 0x03, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ]
+ 0x0E, 0x00, 0x18, 0x00, 0x00, 0x00, 0x78, 0x00, 0xC0, 0x00, 0xF8, 0x03, 0xC0, 0xC0, 0xC0, 0x0F, 0xC0, 0xC0, 0x01, 0x7F, 0xE0, 0x80, 0x03, 0xF8, 0x79, 0x00, 0x03, 0xC0, 0x3F, 0x00, 0x03, 0xC0, 0x0F, 0x00, 0x03, 0xF8, 0x01, 0x00, 0x03, 0x7E, 0x00, 0x80, 0xC3, 0x0F, 0x00, 0xC0, 0xF9, 0x03, 0x00, 0xC0, 0x78, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char `
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char w
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char {
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char |
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char }
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char €
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‚
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ƒ
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char „
+ 0x01, 0xC0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char …
+ 0x01, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char †
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‡
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ˆ
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‰
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Š
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ‹
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Œ
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Ž
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0xFF, 0xFF, 0x03, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0xF8, 0xFF, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ‘
+#endif
+};
+
+extern const LcdFont font28x32 =
+{
+ Liberation_Sans28x32, // font data
+ 0x0020, // first character code
+#if USE_CYRILLIC_CHARACTERS
+ 0x0491, // TODO this won't work yet because the code doesn't take account of the gap from 0x180 to 0x400 inclusive!
+#else
+ 0x017F, // last character code
+#endif
+ 32, // row height in pixels
+ 28, // character width in pixels
+ 3 // number of space columns between characters before kerning
+};
+
+#endif
+
+// End
diff --git a/src/Display/Lcd/Fonts/glcd7x11.cpp b/src/Display/Lcd/Fonts/glcd7x11.cpp
index 30e521a1..b964d4a4 100644
--- a/src/Display/Lcd/Fonts/glcd7x11.cpp
+++ b/src/Display/Lcd/Fonts/glcd7x11.cpp
@@ -7,7 +7,7 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
#include "LcdFont.h"
diff --git a/src/Display/Lcd/ILI9488/ILI9488.cpp b/src/Display/Lcd/ILI9488/ILI9488.cpp
new file mode 100644
index 00000000..7fe8b8e1
--- /dev/null
+++ b/src/Display/Lcd/ILI9488/ILI9488.cpp
@@ -0,0 +1,235 @@
+/*
+ * ILI9488.cpp
+ *
+ * Created on: 6 May 2022
+ * Author: David
+ */
+
+#include "ILI9488.h"
+#include <AnalogOut.h>
+
+#if SUPPORT_ILI9488_LCD
+
+constexpr PwmFrequency BacklightPwmFrequency = 1000;
+
+#if USE_FONT_CHIP
+LcdILI9488::LcdILI9488(Pin fontCsPin, uint8_t sercomNum) noexcept
+ : TFTLcd(320, 480, fontCsPin, SpiMode::mode0, sercomNum)
+#else
+LcdILI9488::LcdILI9488(const LcdFont * const fnts[], size_t nFonts, uint8_t sercomNum) noexcept
+ : TFTLcd(320, 480, fnts, nFonts, SpiMode::mode0, sercomNum)
+#endif
+{
+}
+
+LcdILI9488::~LcdILI9488()
+{
+ AnalogOut::Write(LcdBacklightPin, 0.0, BacklightPwmFrequency);
+}
+
+// Get the display type
+const char *_ecv_array LcdILI9488::GetDisplayTypeName() const noexcept
+{
+ return "480x320 TFT with ILI9488 controller";
+}
+
+// Initialise the TFT screen
+void LcdILI9488::HardwareInit() noexcept
+{
+ const uint16_t Init1[] = { CmdPositiveGammaControl, 0x100, 0x103, 0x109, 0x108, 0x116, 0x10A, 0x13F, 0x178, 0x14C, 0x109, 0x10A, 0x108, 0x116, 0x11A, 0x100 };
+ const uint16_t Init2[] = { CmdNegativeGammaControl, 0x100, 0x116, 0x119, 0x103, 0x10F, 0x105, 0x132, 0x145, 0x146, 0x104, 0x10E, 0x10D, 0x135, 0x137, 0x10F };
+ const uint16_t Init3[] = { CmdPowerControl1, 0x117, 0x115 }; // Power Control 1, Vreg1out, Vreg2out
+ const uint16_t Init4[] = { CmdPowerControl2, 0x141 }; // Power Control 2, VGH, VGL
+ const uint16_t Init5[] = { CmdVComControl1, 0x100, 0x112, 0x180 }; // Power Control 3, Vcom
+ const uint16_t Init6[] = { CmdMemoryAccessControl, 0x48 }; // Memory Access
+ const uint16_t Init7[] = { CmdInterfacePixelFormat, 0x166 }; // Interface Pixel Format 18 bit
+ const uint16_t Init8[] = { CmdInterfaceModeControl, 0x180 }; // Interface Mode Control SDO not used
+ const uint16_t Init9[] = { CmdFrameRateControlNormal, 0x1A0 }; // Frame rate 60Hz
+ const uint16_t Init10[] = { CmdDisplayInversionControl, 0x102 }; // Display Inversion Control 2-dot
+ const uint16_t Init11[] = { CmdDisplayFunctionControl, 0x102, 0x102 }; // Display Function Control RGB/MCU Interface Control, MCU, Source, Gate scan direction
+ const uint16_t Init12[] = { CmdSetImageFunction, 0x100 }; // Set Image Function, Disable 24 bit data
+ const uint16_t Init13[] = { CmdAdjustControl3, 0xA9, 0x51, 0x2C, 0x82 }; // Adjust Control, D7 stream, loose
+
+ SendCommand(CmdReset);
+ delay(ResetDelayMillis + 1);
+
+ SendBuffer(Init1, ARRAY_SIZE(Init1));
+ SendBuffer(Init2, ARRAY_SIZE(Init2));
+ SendBuffer(Init3, ARRAY_SIZE(Init3));
+ SendBuffer(Init4, ARRAY_SIZE(Init4));
+ SendBuffer(Init5, ARRAY_SIZE(Init5));
+ SendBuffer(Init6, ARRAY_SIZE(Init6));
+ SendBuffer(Init7, ARRAY_SIZE(Init7));
+ SendBuffer(Init8, ARRAY_SIZE(Init8));
+ SendBuffer(Init9, ARRAY_SIZE(Init9));
+ SendBuffer(Init10, ARRAY_SIZE(Init10));
+ SendBuffer(Init11, ARRAY_SIZE(Init11));
+ SendBuffer(Init12, ARRAY_SIZE(Init12));
+ SendBuffer(Init13, ARRAY_SIZE(Init13));
+
+ SendCommand(0x11); // Sleep out
+ delay(120);
+
+ currentRowColMode = 2; // force row or column mode to be selected
+ ClearBlock(0, 0, numRows, numCols, false);
+ SendCommand(CmdDisplayOn);
+ AnalogOut::Write(LcdBacklightPin, 1.0, BacklightPwmFrequency);
+}
+
+// Clear part of the display
+void LcdILI9488::ClearBlock(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, bool foreground) noexcept
+{
+ if (left < right && top < bottom)
+ {
+ // Send the data in chunks that fit in our buffer.
+ // There is no need to fill the buffer more than once, we can re-use the same pixel data for each chunk.
+ // The Memory Write Continue command always writes in column mode.
+ uint32_t pixelsLeft = (bottom - top) * (right - left);
+ uint16_t *_ecv_array p = SetColumnMode(spiBuffer, true);
+ uint16_t *_ecv_array const commandPtr = SetGraphicsAddress(p, top, bottom - 1, left, right - 1);
+ *commandPtr = CmdMemoryWrite;
+ uint32_t pixelsToDo = min<uint32_t>(pixelsLeft, MaxPixelsPerTransaction);
+ p = SetPixelData(commandPtr + 1, (foreground) ? fgColour : bgColour, pixelsToDo);
+ SendBuffer(spiBuffer, p - spiBuffer); // send first chunk
+ while ((pixelsLeft -= pixelsToDo) != 0) // update pixelsLeft and check if more to do
+ {
+ *commandPtr = CmdMemoryWriteContinue;
+ if (pixelsLeft < pixelsToDo)
+ {
+ pixelsToDo = pixelsLeft;
+ }
+ SendBuffer(commandPtr, (3 * pixelsToDo) + 1);
+ }
+ }
+}
+
+// Set, clear or invert a pixel
+// x = x-coordinate of the pixel, measured from left hand edge of the display
+// y = y-coordinate of the pixel, measured down from the top of the display
+// mode = whether we want to set or clear the pixel
+void LcdILI9488::SetPixel(PixelNumber y, PixelNumber x, bool mode) noexcept
+{
+ // When writing a single pixel we don't care whether we are in row or column mode
+ uint16_t *_ecv_array p = SetGraphicsAddress(spiBuffer, y, y, x, x);
+ *p++ = CmdMemoryWrite;
+ p = SetPixelData(p, (mode) ? fgColour : bgColour, 1);
+ SendBuffer(spiBuffer, p - spiBuffer);
+}
+
+// Draw a bitmap
+// x0 = x-coordinate of the top left, measured from left hand edge of the display. Currently, must be a multiple of 8.
+// y0 = y-coordinate of the top left, measured down from the top of the display
+// width = width of bitmap in pixels. Currently, must be a multiple of 8.
+// rows = height of bitmap in pixels
+// data = bitmap image, must be ((width/8) * rows) bytes long
+void LcdILI9488::BitmapImage(PixelNumber top, PixelNumber left, PixelNumber height, PixelNumber width, const uint8_t data[]) noexcept
+{
+ //TODO
+}
+
+// Draw a bitmap row
+// x0 = x-coordinate of the top left, measured from left hand edge of the display
+// y0 = y-coordinate of the top left, measured down from the top of the display
+// width = width of bitmap in pixels
+// data = bitmap image, must be ((width + 7)/8) bytes long
+void LcdILI9488::BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept
+{
+ //TODO
+}
+
+// Start a character at the current row and column, clearing the specified number of space columns
+void LcdILI9488::StartCharacter(PixelNumber ySize, PixelNumber numSpaceColumns, PixelNumber numFontColumns) noexcept
+{
+ uint16_t *_ecv_array p = SetColumnMode(spiBuffer, true);
+ p = SetGraphicsAddress(p, row, row + ySize - 1, column, column + numSpaceColumns + numFontColumns - 1);
+ *p++ = CmdMemoryWrite;
+ bufferPointer = SetPixelData(p, (textInverted) ? fgColour : bgColour, numSpaceColumns * ySize);
+}
+
+// Write one column of character data at (row, column)
+void LcdILI9488::WriteColumnData(PixelNumber ySize, uint32_t columnData) noexcept
+{
+ uint16_t *_ecv_array p = bufferPointer;
+ for (PixelNumber i = 0; i < ySize; ++i)
+ {
+ p = SetPixelData(p, (columnData & 1u) ? fgColour : bgColour, 1);
+ columnData >>= 1;
+ }
+ bufferPointer = p;
+}
+
+// Finish writing a character
+void LcdILI9488::EndCharacter() noexcept
+{
+ SendBuffer(spiBuffer, bufferPointer - spiBuffer);
+ bufferPointer = spiBuffer;
+}
+
+// Send a parameterless command
+void LcdILI9488::SendCommand(uint8_t cmd) noexcept
+{
+ spiBuffer[0] = cmd;
+ SendBuffer(spiBuffer, 1);
+}
+
+uint16_t *_ecv_array LcdILI9488::SetGraphicsAddress(uint16_t *_ecv_array buffer, PixelNumber rBegin, PixelNumber rEnd, PixelNumber cBegin, PixelNumber cEnd) noexcept
+{
+ buffer[0] = CmdPageAddressSet;
+ buffer[1] = (cBegin >> 8) | 0x0100;
+ buffer[2] = (cBegin & 0x00FF) | 0x0100;
+ buffer[3] = (cEnd >> 8) | 0x0100;
+ buffer[4] = (cEnd & 0x00FF) | 0x0100;
+ buffer[5] = CmdColumnAddressSet;
+ buffer[6] = (rBegin >> 8) | 0x0100;
+ buffer[7] = (rBegin & 0x00FF) | 0x0100;
+ buffer[8] = (rEnd >> 8) | 0x0100;
+ buffer[9] = (rEnd & 0x00FF) | 0x0100;
+ return buffer + 10;
+}
+
+uint16_t *_ecv_array LcdILI9488::SetColumnMode(uint16_t *_ecv_array buffer, bool columnMode) noexcept
+{
+ if (currentRowColMode == (uint8_t)columnMode)
+ {
+ buffer[0] = CmdMemoryAccessControl;
+ buffer[1] = ((uint16_t)columnMode << 5) | 0x0100;
+ currentRowColMode = (uint8_t)columnMode;
+ return buffer + 2;
+ }
+ else
+ {
+ return buffer;
+ }
+}
+
+uint16_t *_ecv_array LcdILI9488::SetPixelData(uint16_t *_ecv_array buffer, Colour pixelColour, unsigned int numPixels) noexcept
+{
+ const uint16_t blueVal = (pixelColour.blue << 2) | 0x0100;
+ const uint16_t greenVal = (pixelColour.green << 2) | 0x0100;
+ const uint32_t redVal = (pixelColour.red << 2) | 0x0100;
+ while (numPixels != 0
+ && buffer + 3 <= spiBuffer + ARRAY_SIZE(spiBuffer) // should always be true, but don't trust the caller!
+ )
+ {
+ // On the ER-TFTM035-6 display the red and blue pixels appear to be swapped, so we must send them in the order blue-green-red
+ buffer[0] = blueVal;
+ buffer[1] = greenVal;
+ buffer[2] = redVal;
+ buffer += 3;
+ --numPixels;
+ }
+ return buffer;
+}
+
+void LcdILI9488::SendBuffer(const uint16_t *_ecv_array buf, size_t numWords) noexcept
+{
+ digitalWrite(csPin, csPol);
+ delayMicroseconds(1); // ILI9488 needs >= 60ns from CS falling to rising edge of clock
+ spiDev.TransceivePacketNineBit(buf, nullptr, numWords);
+ delayMicroseconds(1); // ILI9488 needs >= 15ns from last falling edge of clock to CS rising
+ digitalWrite(csPin, !csPol);
+}
+
+#endif
+
+// End
diff --git a/src/Display/Lcd/ILI9488/ILI9488.h b/src/Display/Lcd/ILI9488/ILI9488.h
new file mode 100644
index 00000000..d5d37d42
--- /dev/null
+++ b/src/Display/Lcd/ILI9488/ILI9488.h
@@ -0,0 +1,121 @@
+/*
+ * ILI9488.h
+ *
+ * Created on: 5 May 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_LCD_ILI9488_ILI9488_H_
+#define SRC_DISPLAY_LCD_ILI9488_ILI9488_H_
+
+#include <Display/Lcd/TFTLcd.h>
+
+#if SUPPORT_ILI9488_LCD
+
+class LcdILI9488 : public TFTLcd
+{
+public:
+ // Construct a GLCD driver
+#if USE_FONT_CHIP
+ LcdILI9488(Pin fontCsPin, uint8_t sercomNum) noexcept;
+#else
+ LcdILI9488(const LcdFont * const fnts[], size_t nFonts, uint8_t sercomNum) noexcept;
+#endif
+
+ ~LcdILI9488();
+
+ // Clear part of the display
+ void ClearBlock(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, bool foreground) noexcept override;
+
+ // Set, clear or invert a pixel
+ // x = x-coordinate of the pixel, measured from left hand edge of the display
+ // y = y-coordinate of the pixel, measured down from the top of the display
+ // mode = whether we want to set or clear the pixel
+ void SetPixel(PixelNumber y, PixelNumber x, bool mode) noexcept override;
+
+ // Draw a bitmap
+ // x0 = x-coordinate of the top left, measured from left hand edge of the display. Currently, must be a multiple of 8.
+ // y0 = y-coordinate of the top left, measured down from the top of the display
+ // width = width of bitmap in pixels. Currently, must be a multiple of 8.
+ // rows = height of bitmap in pixels
+ // data = bitmap image, must be ((width/8) * rows) bytes long
+ void BitmapImage(PixelNumber top, PixelNumber left, PixelNumber height, PixelNumber width, const uint8_t data[]) noexcept override;
+
+ // Draw a bitmap row
+ // x0 = x-coordinate of the top left, measured from left hand edge of the display
+ // y0 = y-coordinate of the top left, measured down from the top of the display
+ // width = width of bitmap in pixels
+ // data = bitmap image, must be ((width + 7)/8) bytes long
+ void BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept override;
+
+ // Get the display type
+ const char *_ecv_array GetDisplayTypeName() const noexcept override;
+
+protected:
+ // Initialise the TFT screen
+ void HardwareInit() noexcept override;
+
+ // Start a character at the current row and column, clearing the specified number of space columns
+ void StartCharacter(PixelNumber ySize, PixelNumber totalWidth, PixelNumber numSpaceColumns) noexcept override;
+
+ // Write one column of character data at (row, column)
+ void WriteColumnData(PixelNumber ySize, uint32_t columnData) noexcept override;
+
+ // Finish writing a character
+ void EndCharacter() noexcept override final;
+
+private:
+ void SendCommand(uint8_t cmd) noexcept;
+ void SendBuffer(const uint16_t *_ecv_array buf, size_t numWords) noexcept;
+
+ // Functions for setting up commands and data in the buffer. Each one takes the address to store it in the buffer and returns the next available buffer address.
+ uint16_t *_ecv_array SetGraphicsAddress(uint16_t *_ecv_array buffer, PixelNumber rBegin, PixelNumber rEnd, PixelNumber cBegin, PixelNumber cEnd) noexcept;
+ uint16_t *_ecv_array SetColumnMode(uint16_t *_ecv_array buffer, bool columnMode) noexcept;
+ uint16_t *_ecv_array SetPixelData(uint16_t *_ecv_array buffer, Colour pixelColour, unsigned int numPixels) noexcept;
+
+ static constexpr unsigned int MaxPixelsPerTransaction = 1024; // enough for two entire rows (960) and for one 32x28 character plus 3 space columns (992)
+
+ uint16_t spiBuffer[2 + 10 + 1 + (3 * MaxPixelsPerTransaction)]; // large enough to set row or column mode, set the address, and write MaxPixelsPerTransaction
+ uint16_t *_ecv_array bufferPointer = spiBuffer; // tracks the next free location when writing characters
+ uint8_t currentRowColMode;
+
+ static constexpr uint8_t CmdReset = 0x01;
+ static constexpr uint8_t CmdDisplayOn = 0x29;
+ static constexpr uint8_t CmdColumnAddressSet = 0x2A;
+ static constexpr uint8_t CmdPageAddressSet = 0x2B;
+ static constexpr uint8_t CmdMemoryWrite = 0x2C;
+ static constexpr uint8_t CmdMemoryAccessControl = 0x36;
+ static constexpr uint8_t CmdInterfacePixelFormat = 0x3A;
+ static constexpr uint8_t CmdMemoryWriteContinue = 0x3C;
+ static constexpr uint8_t CmdInterfaceModeControl = 0xB0;
+ static constexpr uint8_t CmdFrameRateControlNormal = 0xB1;
+ static constexpr uint8_t CmdDisplayInversionControl = 0xB4;
+ static constexpr uint8_t CmdDisplayFunctionControl = 0xB6;
+ static constexpr uint8_t CmdPowerControl1 = 0xC0;
+ static constexpr uint8_t CmdPowerControl2 = 0xC1;
+ static constexpr uint8_t CmdVComControl1 = 0xC5;
+ static constexpr uint8_t CmdPositiveGammaControl = 0xE0;
+ static constexpr uint8_t CmdNegativeGammaControl = 0xE1;
+ static constexpr uint8_t CmdSetImageFunction = 0xE9;
+ static constexpr uint8_t CmdAdjustControl3 = 0xF7;
+
+ static constexpr uint32_t ResetDelayMillis = 5;
+
+// void CommandDelay() noexcept;
+// void DataDelay() noexcept;
+// void SendByte(uint8_t byteToSend) noexcept;
+// void SetGraphicsAddress(unsigned int r, unsigned int c) noexcept;
+// void StartDataTransaction() noexcept;
+// void EndDataTransaction() noexcept;
+// bool FlushRow() noexcept;
+// void SelectDevice() noexcept;
+// void DeselectDevice() noexcept;
+
+// constexpr static unsigned int CommandDelayMicros = 72 - 8; // 72us required, less 7us time to send the command @ 2.0MHz
+// constexpr static unsigned int DataDelayMicros = 4; // delay between sending data bytes
+// constexpr static unsigned int FlushRowDelayMicros = 20; // Delay between sending each rows when flushing all rows @ 2.0MHz (@ 1.0MHz this is not necessary)
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_LCD_ILI9488_ILI9488_H_ */
diff --git a/src/Display/Lcd/Lcd.cpp b/src/Display/Lcd/Lcd.cpp
index ad8df632..6eab8398 100644
--- a/src/Display/Lcd/Lcd.cpp
+++ b/src/Display/Lcd/Lcd.cpp
@@ -3,56 +3,37 @@
#include "Lcd.h"
-#if SUPPORT_12864_LCD
-
-#include <Hardware/SharedSpi/SharedSpiDevice.h>
-
-Lcd::Lcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts, SpiMode mode) noexcept
- : device(SharedSpiDevice::GetMainSharedSpiDevice(), LcdSpiClockFrequency, mode, NoPin, true),
+#if SUPPORT_DIRECT_LCD
+
+#if USE_FONT_CHIP
+Lcd::Lcd(PixelNumber nr, PixelNumber nc, Pin fontCsPin) noexcept
+ : fontChip(fontCsPin),
+#else
+Lcd::Lcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts) noexcept
+ : fonts(fnts), numFonts(nFonts),
+#endif
numRows(nr), numCols(nc),
- fonts(fnts), numFonts(nFonts)
+ currentFontNumber(0), textInverted(false), numContinuationBytesLeft(0)
{
- imageSize = nr * ((nc + 7)/8);
- image = new uint8_t[imageSize];
}
Lcd::~Lcd()
{
- delete image;
- pinMode(csPin, INPUT_PULLUP);
- pinMode(a0Pin, INPUT_PULLUP);
}
-// Initialise. a0Pin is only used by the ST7567.
-void Lcd::Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept
+// Return the number of fonts
+size_t Lcd::GetNumFonts() const noexcept
{
- // All this is SPI-display specific hardware initialisation, which prohibits I2C-display or UART-display support.
- // NOTE: https://github.com/SchmartMaker/RepRapFirmware/tree/ST7565/src/Display did contain this abstraction
- csPin = p_csPin;
- a0Pin = p_a0Pin;
- contrastRatio = p_contrastRatio;
- resistorRatio = p_resistorRatio;
- device.SetClockFrequency(freq);
- device.SetCsPin(csPin);
- device.SetCsPolarity(csPolarity); // normally active high chip select for ST7920, active low for ST7567
- pinMode(csPin, (csPolarity) ? OUTPUT_LOW : OUTPUT_HIGH);
-#ifdef __LPC17xx__
- device.sspChannel = LcdSpiChannel;
+#if USE_FONT_CHIP
+ return fontChip.GetNumFonts();
+#else
+ return numFonts;
#endif
-
- numContinuationBytesLeft = 0;
- textInverted = false;
- startRow = numRows;
- startCol = numCols;
- endRow = endCol = nextFlushRow = 0;
-
- HardwareInit();
- currentFontNumber = 0;
}
void Lcd::SetFont(size_t newFont) noexcept
{
- if (newFont < numFonts)
+ if (newFont < GetNumFonts())
{
currentFontNumber = newFont;
}
@@ -61,32 +42,25 @@ void Lcd::SetFont(size_t newFont) noexcept
// Get the current font height
PixelNumber Lcd::GetFontHeight() const noexcept
{
+#if USE_FONT_CHIP
+ return fontChip.GetFontHeight(currentFontNumber);
+#else
return fonts[currentFontNumber]->height;
+#endif
}
// Get the height of a specified font
PixelNumber Lcd::GetFontHeight(size_t fontNumber) const noexcept
{
- if (fontNumber >= numFonts)
+ if (fontNumber >= GetNumFonts())
{
fontNumber = currentFontNumber;
}
+#if USE_FONT_CHIP
+ return fontChip.GetFontHeight(fontNumber);
+#else
return fonts[fontNumber]->height;
-}
-
-// Flag a rectangle as dirty. Inline because it is called from only two places.
-inline void Lcd::SetRectDirty(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right) noexcept
-{
- if (top < startRow) startRow = top;
- if (bottom > endRow) endRow = bottom;
- if (left < startCol) startCol = left;
- if (right > endCol) endCol = right;
-}
-
-// Flag a pixel as dirty. The r and c parameters must be no greater than NumRows-1 and NumCols-1 respectively.
-void Lcd::SetDirty(PixelNumber r, PixelNumber c) noexcept
-{
- SetRectDirty(r, c, r + 1, c + 1);
+#endif
}
// Write a UTF8 byte.
@@ -157,109 +131,124 @@ size_t Lcd::write(uint8_t c) noexcept
size_t Lcd::writeNative(uint16_t ch) noexcept
{
- const LcdFont * const currentFont = fonts[currentFontNumber];
if (ch == '\n')
{
- SetCursor(row + currentFont->height + 1, leftMargin);
+ SetCursor(row + GetFontHeight() + 1, leftMargin);
}
else
{
- const uint8_t startChar = currentFont->startCharacter;
- const uint8_t endChar = currentFont->endCharacter;
-
- if (ch < startChar || ch > endChar)
+ if (column < rightMargin) // keep column <= rightMargin in the following code
{
- ch = 0x007F; // replace unsupported characters by square box
- }
+#if USE_FONT_CHIP
+ PixelNumber numFontColumns = fontChip.GetCharacter(currentFontNumber, ch);
+ const PixelNumber fontHeight = GetFontHeight();
+#else
+ const LcdFont * const currentFont = fonts[currentFontNumber];
+ const uint16_t startChar = currentFont->startCharacter;
+ const uint16_t endChar = currentFont->endCharacter;
+
+ if (ch < startChar || ch > endChar)
+ {
+ ch = 0x007F; // replace unsupported characters by square box
+ }
- uint8_t ySize = currentFont->height;
- const uint8_t bytesPerColumn = (ySize + 7)/8;
- if (row >= numRows)
- {
- ySize = 0; // we still execute the code, so that the caller can tell how many columns the text will occupy by writing it off-screen
- }
- else if (row + ySize > numRows)
- {
- ySize = numRows - row;
- }
+ const PixelNumber fontHeight = currentFont->height;
+ const PixelNumber fontBytesPerColumn = (fontHeight + 7)/8;
+ const PixelNumber fontBytesPerChar = (fontBytesPerColumn * currentFont->width) + 1;
+ const uint8_t *fontPtr = currentFont->ptr + (fontBytesPerChar * (ch - startChar));
+ PixelNumber numFontColumns = *fontPtr++;
+#endif
+ const uint32_t cmask = (fontHeight < 32) ? (1u << fontHeight) - 1 : 0xFFFFFFFF;
+ PixelNumber columnsLeft = rightMargin - column;
+ PixelNumber numSpaces;
+ if (lastCharColData != 0) // if we have written anything other than spaces
+ {
+ // Decide whether to add space columns first, and whether to add one less then usual (auto-kerning)
+ uint32_t thisCharColData =
+#if USE_FONT_CHIP
+ fontChip.GetColumnData(0);
+#else
+ *reinterpret_cast<const uint32_t*>(fontPtr) & cmask;
+#endif
+ if (thisCharColData == 0) // for characters with deliberate space column at the start, e.g. decimal point
+ {
+ thisCharColData =
+#if USE_FONT_CHIP
+ fontChip.GetColumnData(1);
+#else
+ *reinterpret_cast<const uint32_t*>(fontPtr + fontBytesPerChar) & cmask;
+#endif
+ }
- const uint8_t bytesPerChar = (bytesPerColumn * currentFont->width) + 1;
- const uint8_t *fontPtr = currentFont->ptr + (bytesPerChar * (ch - startChar));
- const uint16_t cmask = (1u << currentFont->height) - 1;
+ numSpaces =
+#if USE_FONT_CHIP
+ fontChip.GetSpacing(currentFontNumber);
+#else
+ currentFont->numSpaces;
+#endif
- uint8_t nCols = *fontPtr++;
- if (lastCharColData != 0) // if we have written anything other than spaces
- {
- uint8_t numSpaces = currentFont->numSpaces;
+ const bool kern = (numSpaces >= 2)
+ ? ((thisCharColData & lastCharColData) == 0)
+ : (((thisCharColData | (thisCharColData << 1)) & (lastCharColData | (lastCharColData << 1))) == 0);
+ if (kern)
+ {
+ --numSpaces; // kern the character pair
+ }
+
+ if (numSpaces > columnsLeft)
+ {
+ numSpaces = columnsLeft;
+ }
+ columnsLeft -= numSpaces;
+ }
+ else
+ {
+ numSpaces = 0;
+ }
- // Decide whether to add a space column first (auto-kerning)
- // We don't add a space column before a space character.
- // We add a space column after a space character if we would have added one between the preceding and following characters.
- uint16_t thisCharColData = *reinterpret_cast<const uint16_t*>(fontPtr) & cmask;
- if (thisCharColData == 0) // for characters with deliberate space column at the start, e.g. decimal point
+ if (numFontColumns > columnsLeft)
{
- thisCharColData = *reinterpret_cast<const uint16_t*>(fontPtr + 2) & cmask;
+ numFontColumns = columnsLeft;
}
- const bool kern = (numSpaces >= 2)
- ? ((thisCharColData & lastCharColData) == 0)
- : (((thisCharColData | (thisCharColData << 1)) & (lastCharColData | (lastCharColData << 1))) == 0);
- if (kern)
+ PixelNumber ySize = fontHeight;
+ if (row >= numRows)
{
- --numSpaces; // kern the character pair
+ ySize = 0; // we still execute the code, so that the caller can tell how many columns the text will occupy by writing it off-screen
}
- if (numSpaces != 0 && column < rightMargin)
+ else if (ySize > numRows - row)
{
- // Add a single space column after the character
- if (ySize != 0)
- {
- const uint8_t mask = 0x80 >> (column & 7);
- uint8_t *p = image + ((row * (numCols/8)) + (column/8));
- for (uint8_t i = 0; i < ySize && p < (image + imageSize); ++i)
- {
- const uint8_t oldVal = *p;
- const uint8_t newVal = (textInverted) ? oldVal | mask : oldVal & ~mask;
- if (newVal != oldVal)
- {
- *p = newVal;
- SetDirty(row + i, column);
- }
- p += (numCols/8);
- }
- }
- ++column;
+ ySize = numRows - row;
}
- }
- while (nCols != 0 && column < rightMargin)
- {
- uint16_t colData = *reinterpret_cast<const uint16_t*>(fontPtr);
- fontPtr += bytesPerColumn;
- if (colData != 0)
+ if (ySize != 0)
{
- lastCharColData = colData & cmask;
+ StartCharacter(ySize, numSpaces, numFontColumns);
}
- if (ySize != 0)
+ column += numSpaces;
+
+ for (PixelNumber charColumn = 0; charColumn < numFontColumns; ++charColumn)
{
- const uint8_t mask1 = 0x80 >> (column & 7);
- const uint8_t mask2 = ~mask1;
- uint8_t *p = image + ((row * (numCols/8)) + (column/8));
- const uint16_t setPixelVal = (textInverted) ? 0 : 1;
- for (uint8_t i = 0; i < ySize; ++i)
+#if USE_FONT_CHIP
+ const uint32_t colData = fontChip.GetColumnData(charColumn);
+#else
+ const uint32_t colData = *reinterpret_cast<const uint32_t*>(fontPtr);
+ fontPtr += fontBytesPerColumn;
+#endif
+ if ((colData & cmask) != 0)
+ {
+ lastCharColData = colData & cmask;
+ }
+ if (ySize != 0)
{
- const uint8_t oldVal = *p;
- const uint8_t newVal = ((colData & 1u) == setPixelVal) ? oldVal | mask1 : oldVal & mask2;
- if (newVal != oldVal)
- {
- *p = newVal;
- SetDirty(row + i, column);
- }
- colData >>= 1;
- p += (numCols/8);
+ WriteColumnData(ySize, colData);
}
+ ++column;
+ }
+ if (ySize != 0)
+ {
+ EndCharacter();
}
- --nCols;
- ++column;
}
justSetCursor = false;
@@ -267,42 +256,30 @@ size_t Lcd::writeNative(uint16_t ch) noexcept
return 1;
}
-// Write a space
-void Lcd::WriteSpaces(PixelNumber numPixels) noexcept
+// Clear part of the display and select non-inverted text.
+void Lcd::Clear(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right) noexcept
{
- const LcdFont * const currentFont = fonts[currentFontNumber];
- uint8_t ySize = currentFont->height;
- if (row >= numRows)
- {
- ySize = 0; // we still execute the code, so that the caller can tell how many columns the text will occupy by writing it off-screen
- }
- else if (row + ySize > numRows)
- {
- ySize = numRows - row;
- }
+ ClearBlock(top, left, bottom, right, false);
- while (numPixels != 0 && column < numCols)
+ SetCursor(top, left);
+ textInverted = false;
+ leftMargin = left;
+ rightMargin = right;
+}
+
+// Write some spaces
+void Lcd::WriteSpaces(PixelNumber numPixels) noexcept
+{
+ uint8_t ySize = GetFontHeight();
+ if (row < numRows)
{
- if (ySize != 0)
+ if (row + ySize > numRows)
{
- const uint8_t mask = 0x80 >> (column & 7);
- uint8_t *p = image + ((row * (numCols/8)) + (column/8));
- for (uint8_t i = 0; i < ySize && p < (image + imageSize); ++i)
- {
- const uint8_t oldVal = *p;
- const uint8_t newVal = (textInverted) ? oldVal | mask : oldVal & ~mask;
- if (newVal != oldVal)
- {
- *p = newVal;
- SetDirty(row + i, column);
- }
- p += (numCols/8);
- }
+ ySize = numRows - row;
}
- --numPixels;
- ++column;
+ ClearBlock(row, column, row + ySize, column + numPixels, textInverted);
}
-
+ column += numPixels;
lastCharColData = 0;
justSetCursor = false;
}
@@ -342,36 +319,9 @@ void Lcd::SetRightMargin(PixelNumber r) noexcept
// Clear a rectangle from the current position to the right margin. The height of the rectangle is the height of the current font.
void Lcd::ClearToMargin() noexcept
{
- const uint8_t fontHeight = fonts[currentFontNumber]->height;
- // TODO make this more efficient by finding the extent of the pixels changed the then calling SetRectDirty just once.
- // Or maybe don't bother, just call SetRectDirty on the whole rectangle
- while (column < rightMargin)
+ if (column < rightMargin)
{
- uint8_t *p = image + ((row * (numCols/8)) + (column/8));
- uint8_t mask = 0xFF >> (column & 7);
- PixelNumber nextColumn;
- if ((column & (~7)) < (rightMargin & (~7)))
- {
- nextColumn = (column & (~7)) + 8;
- }
- else
- {
- mask ^= 0xFF >> (rightMargin & 7);
- nextColumn = rightMargin;
- }
-
- for (uint8_t i = 0; i < fontHeight && p < (image + imageSize); ++i)
- {
- const uint8_t oldVal = *p;
- const uint8_t newVal = (textInverted) ? oldVal | mask : oldVal & ~mask;
- if (newVal != oldVal)
- {
- *p = newVal;
- SetRectDirty(row + i, column, row + i + 1, nextColumn);
- }
- p += (numCols/8);
- }
- column = nextColumn;
+ WriteSpaces(rightMargin - column);
}
}
@@ -383,57 +333,13 @@ void Lcd::TextInvert(bool b) noexcept
textInverted = b;
if (!justSetCursor)
{
- lastCharColData = 0xFFFF; // force a space when switching between normal and inverted text
+ lastCharColData = (GetFontHeight() < 32) ? (1u << GetFontHeight()) - 1 : 0xFFFFFFFF; // force a space when switching between normal and inverted text
}
}
}
-// Clear a rectangular block of pixels starting at rows, scol ending just before erow, ecol
-void Lcd::Clear(PixelNumber sRow, PixelNumber sCol, PixelNumber eRow, PixelNumber eCol) noexcept
-{
- if (eCol > numCols) { eCol = numCols; }
- if (eRow > numRows) { eRow = numRows; }
- if (sCol < eCol && sRow < eRow)
- {
- uint8_t sMask = ~(0xFF >> (sCol & 7)); // mask of bits we want to keep in the first byte of each row that we modify
- const uint8_t eMask = 0xFF >> (eCol & 7); // mask of bits we want to keep in the last byte of each row that we modify
- if ((sCol & ~7) == (eCol & ~7))
- {
- sMask |= eMask; // special case of just clearing some middle bits
- }
- for (PixelNumber r = sRow; r < eRow; ++r)
- {
- uint8_t * p = image + ((r * (numCols/8)) + (sCol/8));
- uint8_t * const endp = image + ((r * (numCols/8)) + (eCol/8));
- *p &= sMask;
- if (p != endp)
- {
- while (++p < endp)
- {
- *p = 0;
- }
- if ((eCol & 7) != 0)
- {
- *p &= eMask;
- }
- }
- }
-
- // Flag cleared part as dirty
- if (sCol < startCol) { startCol = sCol; }
- if (eCol >= endCol) { endCol = eCol; }
- if (sRow < startRow) { startRow = sRow; }
- if (eRow >= endRow) { endRow = eRow; }
-
- SetCursor(sRow, sCol);
- textInverted = false;
- leftMargin = sCol;
- rightMargin = eCol;
- }
-}
-
// Draw a line using the Bresenham Algorithm (thanks Wikipedia)
-void Lcd::Line(PixelNumber y0, PixelNumber x0, PixelNumber y1, PixelNumber x1, PixelMode mode) noexcept
+void Lcd::Line(PixelNumber y0, PixelNumber x0, PixelNumber y1, PixelNumber x1, bool mode) noexcept
{
int dx = (x1 >= x0) ? x1 - x0 : x0 - x1;
int dy = (y1 >= y0) ? y1 - y0 : y0 - y1;
@@ -463,7 +369,7 @@ void Lcd::Line(PixelNumber y0, PixelNumber x0, PixelNumber y1, PixelNumber x1, P
}
// Draw a circle using the Bresenham Algorithm (thanks Wikipedia)
-void Lcd::Circle(PixelNumber x0, PixelNumber y0, PixelNumber radius, PixelMode mode) noexcept
+void Lcd::Circle(PixelNumber x0, PixelNumber y0, PixelNumber radius, bool mode) noexcept
{
int f = 1 - (int)radius;
int ddF_x = 1;
@@ -501,68 +407,6 @@ void Lcd::Circle(PixelNumber x0, PixelNumber y0, PixelNumber radius, PixelMode m
}
}
-// Draw a bitmap. x0 and numCols must be divisible by 8.
-void Lcd::BitmapImage(PixelNumber x0, PixelNumber y0, PixelNumber width, PixelNumber height, const uint8_t data[]) noexcept
-{
- for (PixelNumber r = 0; r < height && r + y0 < numRows; ++r)
- {
- uint8_t *p = image + (((r + y0) * (numCols/8)) + (x0/8));
- uint16_t bitMapOffset = r * (width/8);
- for (PixelNumber c = 0; c < (width/8) && c + (x0/8) < numCols/8; ++c)
- {
- *p++ = data[bitMapOffset++];
- }
- }
-
- // Assume the whole area has changed
- if (x0 < startCol) startCol = x0;
- if (x0 + width > endCol) endCol = x0 + width;
- if (y0 < startRow) startRow = y0;
- if (y0 + height > endRow) endRow = y0 + height;
-}
-
-// Draw a single bitmap row. 'left' and 'width' do not need to be divisible by 8.
-void Lcd::BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept
-{
- if (width != 0 && top < numRows) // avoid possible arithmetic underflow or overflowing the buffer
- {
- const uint8_t inv = (invert) ? 0xFF : 0;
- uint8_t firstColIndex = left/8; // column index of the first byte to write
- const uint8_t lastColIndex = (left + width - 1)/8; // column index of the last byte to write
- const unsigned int firstDataShift = left % 8; // number of bits in the first byte that we leave alone
- uint8_t *p = image + (top * numCols/8) + firstColIndex;
-
- // Do all bytes except the last one
- uint8_t accumulator = *p & (0xFF << (8 - firstDataShift)); // prime the accumulator
- while (firstColIndex < lastColIndex)
- {
- const uint8_t invData = *data ^ inv;
- const uint8_t newVal = accumulator | (invData >> firstDataShift);
- if (newVal != *p)
- {
- *p = newVal;
- SetDirty(top, 8 * firstColIndex);
- }
- accumulator = invData << (8 - firstDataShift);
- ++p;
- ++data;
- ++firstColIndex;
- }
-
- // Do the last byte. 'accumulator' contains up to 'firstDataShift' of the most significant bits.
- const unsigned int lastDataShift = 7 - ((left + width - 1) % 8); // number of trailing bits in the last byte that we leave alone, 0 to 7
- const uint8_t lastMask = (1u << lastDataShift) - 1; // mask for bits we want to keep;
- accumulator |= (*data ^ inv) >> firstDataShift;
- accumulator &= ~lastMask;
- accumulator |= *p & lastMask;
- if (accumulator != *p)
- {
- *p = accumulator;
- SetDirty(top, 8 * firstColIndex);
- }
- }
-}
-
// Flush all of the dirty part of the image to the lcd. Only called during startup and shutdown.
void Lcd::FlushAll() noexcept
{
@@ -581,48 +425,6 @@ void Lcd::SetCursor(PixelNumber r, PixelNumber c) noexcept
justSetCursor = true;
}
-void Lcd::SetPixel(PixelNumber y, PixelNumber x, PixelMode mode) noexcept
-{
- if (y < numRows && x < rightMargin)
- {
- uint8_t * const p = image + ((y * (numCols/8)) + (x/8));
- const uint8_t mask = 0x80u >> (x%8);
- const uint8_t oldVal = *p;
- uint8_t newVal;
- switch(mode)
- {
- case PixelMode::PixelClear:
- newVal = oldVal & ~mask;
- break;
- case PixelMode::PixelSet:
- newVal = oldVal | mask;
- break;
- case PixelMode::PixelFlip:
- newVal = oldVal ^ mask;
- break;
- default:
- newVal = oldVal;
- break;
- }
-
- if (newVal != oldVal)
- {
- *p = newVal;
- SetDirty(y, x);
- }
- }
-}
-
-bool Lcd::ReadPixel(PixelNumber x, PixelNumber y) const noexcept
-{
- if (y < numRows && x < numCols)
- {
- const uint8_t * const p = image + ((y * (numCols/8)) + (x/8));
- return (*p & (0x80u >> (x%8))) != 0;
- }
- return false;
-}
-
#endif
// End
diff --git a/src/Display/Lcd/Lcd.h b/src/Display/Lcd/Lcd.h
index 9fdd0fee..5aa5a5fd 100644
--- a/src/Display/Lcd/Lcd.h
+++ b/src/Display/Lcd/Lcd.h
@@ -3,32 +3,52 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
#include <Print.h>
-#include "Fonts/LcdFont.h"
-#include <Hardware/SharedSpi/SharedSpiClient.h>
+#include <Hardware/Spi/SharedSpiClient.h>
#include <General/SafeVsnprintf.h>
-// Enumeration for specifying drawing modes
-enum class PixelMode : uint8_t
+#if USE_FONT_CHIP
+# include "Fonts/ER3301_1.h"
+#else
+# include "Fonts/LcdFont.h"
+#endif
+
+typedef uint16_t PixelNumber;
+
+struct Colour
{
- PixelClear = 0, // clear the pixel(s)
- PixelSet = 1, // set the pixel(s)
- PixelFlip = 2 // invert the pixel(s)
+ uint32_t red : 6,
+ green : 6,
+ blue : 6;
+
+ constexpr Colour(uint8_t r, uint8_t g, uint8_t b) noexcept : red(r), green(g), blue(b) { }
+ constexpr Colour() noexcept : red(0), green(0), blue(0) { }
};
-typedef uint8_t PixelNumber;
+struct Colours
+{
+ static constexpr Colour Black = Colour(0, 0, 0);
+ static constexpr Colour White = Colour(63, 63, 63);
+ static constexpr Colour Red = Colour(63, 0, 0);
+ static constexpr Colour Green = Colour(0, 63, 0);
+ static constexpr Colour Blue = Colour(0, 0, 63);
+};
-// Class for driving 128x64 graphical LCD fitted with ST7920 controller
-// This drives the GLCD in serial mode so that it needs just 2 pins.
+// Class for driving graphical LCD
// Derive the LCD class from the Print class so that we can print stuff to it in alpha mode
class Lcd
{
public:
// Construct a GLCD driver.
- Lcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts, SpiMode mode) noexcept;
+#if USE_FONT_CHIP
+ Lcd(PixelNumber nr, PixelNumber nc, Pin fontCsPin) noexcept;
+#else
+ Lcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts) noexcept;
+#endif
+
virtual ~Lcd();
// Flush just some data, returning true if this needs to be called again
@@ -38,27 +58,68 @@ public:
virtual const char *GetDisplayTypeName() const noexcept = 0;
// Get the SPI frequency
- uint32_t GetSpiFrequency() const noexcept { return device.GetFrequency(); }
+ virtual uint32_t GetSpiFrequency() const noexcept = 0;
// Initialize the display
- void Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept;
+ virtual void Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept = 0;
+
+ // Set, clear or invert a pixel
+ // x = x-coordinate of the pixel, measured from left hand edge of the display
+ // y = y-coordinate of the pixel, measured down from the top of the display
+ // mode = whether we want to set or clear the pixel
+ virtual void SetPixel(PixelNumber y, PixelNumber x, bool mode) noexcept = 0;
+
+ // Draw a bitmap
+ // x0 = x-coordinate of the top left, measured from left hand edge of the display. Currently, must be a multiple of 8.
+ // y0 = y-coordinate of the top left, measured down from the top of the display
+ // width = width of bitmap in pixels. Currently, must be a multiple of 8.
+ // rows = height of bitmap in pixels
+ // data = bitmap image, must be ((width/8) * rows) bytes long
+ virtual void BitmapImage(PixelNumber top, PixelNumber left, PixelNumber height, PixelNumber width, const uint8_t data[]) noexcept = 0;
+
+ // Draw a bitmap row
+ // x0 = x-coordinate of the top left, measured from left hand edge of the display
+ // y0 = y-coordinate of the top left, measured down from the top of the display
+ // width = width of bitmap in pixels
+ // data = bitmap image, must be ((width + 7)/8) bytes long
+ virtual void BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept = 0;
+
+ // Set the foreground colour. Does nothing on monochrome displays.
+ virtual void SetForegroundColour(Colour col) noexcept = 0;
+
+ // Set the background colour. Does nothing on monochrome displays.
+ virtual void SetBackgroundColour(Colour col) noexcept = 0;
+
+ // Draw a line.
+ // x0 = x-coordinate of one end of the line, measured from left hand edge of the display
+ // y0 = y-coordinate of one end of the line, measured down from the top of the display
+ // x1, y1 = coordinates of the other end od the line
+ // mode = whether we want to set or clear each pixel
+ void Line(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, bool mode) noexcept;
+
+ // Draw a circle
+ // x0 = x-coordinate of the centre, measured from left hand edge of the display
+ // y0 = y-coordinate of the centre, measured down from the top of the display
+ // radius = radius of the circle in pixels
+ // mode = whether we want to set or clear each pixel
+ void Circle(PixelNumber p_row, PixelNumber col, PixelNumber radius, bool mode) noexcept;
// Select the font to use for subsequent calls to write() in graphics mode
void SetFont(size_t newFont) noexcept;
- const PixelNumber GetNumRows() const noexcept { return numRows; }
- const PixelNumber GetNumCols() const noexcept { return numCols; }
+ PixelNumber GetNumRows() const noexcept { return numRows; }
+ PixelNumber GetNumCols() const noexcept { return numCols; }
// Write a single character in the current font. Called by the 'print' functions.
// c = character to write
// Returns the number of characters written (1 if we wrote it, 0 otherwise)
- size_t write(uint8_t c) noexcept; // write a character
+ size_t write(uint8_t c) noexcept;
// Write a space
void WriteSpaces(PixelNumber numPixels) noexcept;
// Return the number of fonts
- size_t GetNumFonts() const noexcept { return numFonts; }
+ size_t GetNumFonts() const noexcept;
// Get the current font height
PixelNumber GetFontHeight() const noexcept;
@@ -73,7 +134,7 @@ public:
void Clear(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right) noexcept;
// Clear the whole display and select non-inverted text.
- void Clear() noexcept
+ void ClearAll() noexcept
{
Clear(0, 0, numRows, numCols);
}
@@ -95,84 +156,50 @@ public:
// Set the right margin. In graphics mode, anything written will be truncated at the right margin. Defaults to the right hand edge of the display.
void SetRightMargin(PixelNumber r) noexcept;
- // Clear a rectangle from the current position to the right margin (graphics mode only). The height of the rectangle is the height of the current font.
+ // Clear a rectangle from the current position to the right margin. The height of the rectangle is the height of the current font.
void ClearToMargin() noexcept;
- // Flush the display buffer to the display. Data will not be committed to the display until this is called.
+ // Flush the display buffer to the display. Data may not be committed to the display until this is called.
void FlushAll() noexcept;
- // Set, clear or invert a pixel
- // x = x-coordinate of the pixel, measured from left hand edge of the display
- // y = y-coordinate of the pixel, measured down from the top of the display
- // mode = whether we want to set, clear or invert the pixel
- void SetPixel(PixelNumber y, PixelNumber x, PixelMode mode) noexcept;
+ // printf to LCD
+ int printf(const char *_ecv_array fmt, ...) noexcept;
- // Read a pixel. Returns true if the pixel is set, false if it is clear.
- // x = x-coordinate of the pixel, measured from left hand edge of the display
- // y = y-coordinate of the pixel, measured down from the top of the display
- bool ReadPixel(PixelNumber y, PixelNumber x) const noexcept;
+protected:
+ // Clear part of the display
+ virtual void ClearBlock(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, bool foreground) noexcept = 0;
- // Draw a line.
- // x0 = x-coordinate of one end of the line, measured from left hand edge of the display
- // y0 = y-coordinate of one end of the line, measured down from the top of the display
- // x1, y1 = coordinates of the other end od the line
- // mode = whether we want to set, clear or invert each pixel
- void Line(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, PixelMode mode) noexcept;
+ // Start a character at the current row and column, clearing the specified number of space columns
+ virtual void StartCharacter(PixelNumber ySize, PixelNumber numSpaceColumns, PixelNumber numFontColumns) noexcept = 0;
- // Draw a circle
- // x0 = x-coordinate of the centre, measured from left hand edge of the display
- // y0 = y-coordinate of the centre, measured down from the top of the display
- // radius = radius of the circle in pixels
- // mode = whether we want to set, clear or invert each pixel
- void Circle(PixelNumber p_row, PixelNumber col, PixelNumber radius, PixelMode mode) noexcept;
+ // Write one column of character data at (row, column)
+ virtual void WriteColumnData(PixelNumber ySize, uint32_t columnData) noexcept = 0;
- // Draw a bitmap
- // x0 = x-coordinate of the top left, measured from left hand edge of the display. Currently, must be a multiple of 8.
- // y0 = y-coordinate of the top left, measured down from the top of the display
- // width = width of bitmap in pixels. Currently, must be a multiple of 8.
- // rows = height of bitmap in pixels
- // data = bitmap image, must be ((width/8) * rows) bytes long
- void BitmapImage(PixelNumber top, PixelNumber left, PixelNumber height, PixelNumber width, const uint8_t data[]) noexcept;
-
- // Draw a bitmap row
- // x0 = x-coordinate of the top left, measured from left hand edge of the display
- // y0 = y-coordinate of the top left, measured down from the top of the display
- // width = width of bitmap in pixels
- // data = bitmap image, must be ((width + 7)/8) bytes long
- void BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept;
-
- // printf to LCD
- int printf(const char* fmt, ...) noexcept;
+ // Finish writing a character
+ virtual void EndCharacter() noexcept = 0;
-protected:
- virtual void HardwareInit() noexcept = 0;
+ // Write a decoded character
+ size_t writeNative(uint16_t c) noexcept;
- size_t writeNative(uint16_t c) noexcept; // write a decoded character
- void SetDirty(PixelNumber r, PixelNumber c) noexcept;
- void SetRectDirty(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right) noexcept;
+#if USE_FONT_CHIP
+ ER3301_1 fontChip;
+#else
+ const LcdFont * const *_ecv_array fonts;
+ const size_t numFonts;
+#endif
- size_t imageSize;
- uint8_t *image; // image buffer
- SharedSpiClient device;
const PixelNumber numRows, numCols;
- Pin csPin;
- Pin a0Pin;
- uint8_t contrastRatio;
- uint8_t resistorRatio;
- PixelNumber startRow, startCol, endRow, endCol; // coordinates of the dirty rectangle
- PixelNumber nextFlushRow; // which row we need to flush next
+ PixelNumber row, column;
+ PixelNumber leftMargin, rightMargin;
+
+ size_t currentFontNumber = 0; // index of the current font
+ bool textInverted = false;
private:
- const LcdFont * const *fonts;
- const size_t numFonts;
- size_t currentFontNumber; // index of the current font
uint32_t charVal;
- uint16_t lastCharColData; // data for the last non-space column, used for kerning
- uint8_t numContinuationBytesLeft;
- PixelNumber row, column;
- PixelNumber leftMargin, rightMargin;
- bool textInverted;
- bool justSetCursor;
+ uint32_t lastCharColData = 0; // data for the last non-space column, used for kerning
+ uint8_t numContinuationBytesLeft = 0;
+ bool justSetCursor = false;
};
#endif
diff --git a/src/Display/Lcd/MonoLcd.cpp b/src/Display/Lcd/MonoLcd.cpp
new file mode 100644
index 00000000..67259e5a
--- /dev/null
+++ b/src/Display/Lcd/MonoLcd.cpp
@@ -0,0 +1,241 @@
+/*
+ * MonoLcd.cpp
+ *
+ * Created on: 6 May 2022
+ * Author: David
+ */
+
+#include "MonoLcd.h"
+#include <Hardware/Spi/SharedSpiDevice.h>
+
+#if SUPPORT_12864_LCD
+
+MonoLcd::MonoLcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts, SpiMode mode) noexcept
+ : Lcd(nr, nc, fnts, nFonts),
+ device(SharedSpiDevice::GetMainSharedSpiDevice(), LcdSpiClockFrequency, mode, NoPin, true)
+{
+ imageSize = nr * ((nc + 7)/8);
+ image = new uint8_t[imageSize];
+}
+
+MonoLcd::~MonoLcd()
+{
+ delete image;
+ pinMode(csPin, INPUT_PULLUP);
+ pinMode(a0Pin, INPUT_PULLUP);
+}
+
+// Initialise. a0Pin is only used by the ST7567.
+void MonoLcd::Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept
+{
+ // All this is SPI-display specific hardware initialisation, which prohibits I2C-display or UART-display support.
+ // NOTE: https://github.com/SchmartMaker/RepRapFirmware/tree/ST7565/src/Display did contain this abstraction
+ csPin = p_csPin;
+ a0Pin = p_a0Pin;
+ contrastRatio = p_contrastRatio;
+ resistorRatio = p_resistorRatio;
+ device.SetClockFrequency(freq);
+ device.SetCsPin(csPin);
+ device.SetCsPolarity(csPolarity); // normally active high chip select for ST7920, active low for ST7567
+ pinMode(csPin, (csPolarity) ? OUTPUT_LOW : OUTPUT_HIGH);
+#ifdef __LPC17xx__
+ device.sspChannel = LcdSpiChannel;
+#endif
+
+ startRow = numRows;
+ startCol = numCols;
+ endRow = endCol = nextFlushRow = 0;
+
+ HardwareInit();
+}
+
+// Clear a rectangular block of pixels starting at top, left ending just before bottom, right
+void MonoLcd::ClearBlock(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, bool foreground) noexcept
+{
+ if (right > numCols) { right = numCols; }
+ if (bottom > numRows) { bottom = numRows; }
+ if (left < right && top < bottom)
+ {
+ uint8_t sMask = ~(0xFF >> (left & 7)); // mask of bits we want to keep in the first byte of each row that we modify
+ const uint8_t eMask = 0xFF >> (right & 7); // mask of bits we want to keep in the last byte of each row that we modify
+ if ((left & ~7) == (right & ~7))
+ {
+ sMask |= eMask; // special case of just clearing some middle bits
+ }
+ for (PixelNumber r = top; r < bottom; ++r)
+ {
+ uint8_t * p = image + ((r * (numCols/8)) + (left/8));
+ uint8_t * const endp = image + ((r * (numCols/8)) + (right/8));
+ if (foreground)
+ {
+ *p |= ~sMask;
+ }
+ else
+ {
+ *p &= sMask;
+ }
+ if (p != endp)
+ {
+ while (++p < endp)
+ {
+ *p = (foreground) ? 0xFF : 0;
+ }
+ if ((right & 7) != 0)
+ {
+ if (foreground)
+ {
+ *p |= ~eMask;
+ }
+ else
+ {
+ *p &= eMask;
+ }
+ }
+ }
+ }
+
+ SetRectDirty(top, left, bottom, right); // flag cleared part as dirty
+ }
+}
+
+// Flag a rectangle as dirty. Inline because it is called from only two places.
+inline void MonoLcd::SetRectDirty(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right) noexcept
+{
+ if (top < startRow) startRow = top;
+ if (bottom > endRow) endRow = bottom;
+ if (left < startCol) startCol = left;
+ if (right > endCol) endCol = right;
+}
+
+// Flag a pixel as dirty. The r and c parameters must be no greater than NumRows-1 and NumCols-1 respectively.
+void MonoLcd::SetDirty(PixelNumber r, PixelNumber c) noexcept
+{
+ SetRectDirty(r, c, r + 1, c + 1);
+}
+
+// Start a character at the current row and column, clearing the specified number of space columns
+void MonoLcd::StartCharacter(PixelNumber ySize, PixelNumber numSpaceColumns, PixelNumber numFontColumns) noexcept
+{
+ if (numSpaceColumns != 0)
+ {
+ ClearBlock(row, column, row + ySize, column + numSpaceColumns, textInverted);
+ }
+}
+
+// Write one column of character data at (row, column)
+void MonoLcd::WriteColumnData(PixelNumber ySize, uint32_t columnData) noexcept
+{
+ const uint8_t mask1 = 0x80 >> (column & 7);
+ const uint8_t mask2 = ~mask1;
+ uint8_t *p = image + ((row * (numCols/8)) + (column/8));
+ const uint16_t setPixelVal = (textInverted) ? 0 : 1;
+ for (uint8_t i = 0; i < ySize; ++i)
+ {
+ const uint8_t oldVal = *p;
+ const uint8_t newVal = ((columnData & 1u) == setPixelVal) ? oldVal | mask1 : oldVal & mask2;
+ if (newVal != oldVal)
+ {
+ *p = newVal;
+ SetDirty(row + i, column);
+ }
+ columnData >>= 1;
+ p += (numCols/8);
+ }
+}
+
+// Finish writing a character
+void MonoLcd::EndCharacter() noexcept
+{
+ // Nothing needed here
+}
+
+void MonoLcd::SetPixel(PixelNumber y, PixelNumber x, bool mode) noexcept
+{
+ if (y < numRows && x < rightMargin)
+ {
+ uint8_t * const p = image + ((y * (numCols/8)) + (x/8));
+ const uint8_t mask = 0x80u >> (x%8);
+ const uint8_t oldVal = *p;
+ uint8_t newVal;
+ if (mode)
+ {
+ newVal = oldVal | mask;
+ }
+ else
+ {
+ newVal = oldVal & ~mask;
+ }
+
+ if (newVal != oldVal)
+ {
+ *p = newVal;
+ SetDirty(y, x);
+ }
+ }
+}
+
+// Draw a bitmap. x0 and numCols must be divisible by 8.
+void MonoLcd::BitmapImage(PixelNumber x0, PixelNumber y0, PixelNumber width, PixelNumber height, const uint8_t data[]) noexcept
+{
+ for (PixelNumber r = 0; r < height && r + y0 < numRows; ++r)
+ {
+ uint8_t *p = image + (((r + y0) * (numCols/8)) + (x0/8));
+ uint16_t bitMapOffset = r * (width/8);
+ for (PixelNumber c = 0; c < (width/8) && c + (x0/8) < numCols/8; ++c)
+ {
+ *p++ = data[bitMapOffset++];
+ }
+ }
+
+ // Assume the whole area has changed
+ if (x0 < startCol) startCol = x0;
+ if (x0 + width > endCol) endCol = x0 + width;
+ if (y0 < startRow) startRow = y0;
+ if (y0 + height > endRow) endRow = y0 + height;
+}
+
+// Draw a single bitmap row. 'left' and 'width' do not need to be divisible by 8.
+void MonoLcd::BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept
+{
+ if (width != 0 && top < numRows) // avoid possible arithmetic underflow or overflowing the buffer
+ {
+ const uint8_t inv = (invert) ? 0xFF : 0;
+ uint8_t firstColIndex = left/8; // column index of the first byte to write
+ const uint8_t lastColIndex = (left + width - 1)/8; // column index of the last byte to write
+ const unsigned int firstDataShift = left % 8; // number of bits in the first byte that we leave alone
+ uint8_t *p = image + (top * numCols/8) + firstColIndex;
+
+ // Do all bytes except the last one
+ uint8_t accumulator = *p & (0xFF << (8 - firstDataShift)); // prime the accumulator
+ while (firstColIndex < lastColIndex)
+ {
+ const uint8_t invData = *data ^ inv;
+ const uint8_t newVal = accumulator | (invData >> firstDataShift);
+ if (newVal != *p)
+ {
+ *p = newVal;
+ SetDirty(top, 8 * firstColIndex);
+ }
+ accumulator = invData << (8 - firstDataShift);
+ ++p;
+ ++data;
+ ++firstColIndex;
+ }
+
+ // Do the last byte. 'accumulator' contains up to 'firstDataShift' of the most significant bits.
+ const unsigned int lastDataShift = 7 - ((left + width - 1) % 8); // number of trailing bits in the last byte that we leave alone, 0 to 7
+ const uint8_t lastMask = (1u << lastDataShift) - 1; // mask for bits we want to keep;
+ accumulator |= (*data ^ inv) >> firstDataShift;
+ accumulator &= ~lastMask;
+ accumulator |= *p & lastMask;
+ if (accumulator != *p)
+ {
+ *p = accumulator;
+ SetDirty(top, 8 * firstColIndex);
+ }
+ }
+}
+
+#endif
+
+// End
diff --git a/src/Display/Lcd/MonoLcd.h b/src/Display/Lcd/MonoLcd.h
new file mode 100644
index 00000000..ee9f0aa8
--- /dev/null
+++ b/src/Display/Lcd/MonoLcd.h
@@ -0,0 +1,88 @@
+/*
+ * MonoLcd.h
+ *
+ * Created on: 6 May 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_LCD_MONOLCD_H_
+#define SRC_DISPLAY_LCD_MONOLCD_H_
+
+#include "Lcd.h"
+
+#if SUPPORT_12864_LCD
+
+class MonoLcd : public Lcd
+{
+public:
+ MonoLcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts, SpiMode mode) noexcept;
+ virtual ~MonoLcd();
+
+ // Get the SPI frequency
+ uint32_t GetSpiFrequency() const noexcept override final { return device.GetFrequency(); }
+
+ // Initialize the display
+ void Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept override final;
+
+ // Clear part of the display
+ void ClearBlock(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right, bool foreground) noexcept override final;
+
+ // Set, clear or invert a pixel
+ // x = x-coordinate of the pixel, measured from left hand edge of the display
+ // y = y-coordinate of the pixel, measured down from the top of the display
+ // mode = whether we want to set or clear the pixel
+ void SetPixel(PixelNumber y, PixelNumber x, bool mode) noexcept override final;
+
+ // Draw a bitmap
+ // x0 = x-coordinate of the top left, measured from left hand edge of the display. Currently, must be a multiple of 8.
+ // y0 = y-coordinate of the top left, measured down from the top of the display
+ // width = width of bitmap in pixels. Currently, must be a multiple of 8.
+ // rows = height of bitmap in pixels
+ // data = bitmap image, must be ((width/8) * rows) bytes long
+ void BitmapImage(PixelNumber top, PixelNumber left, PixelNumber height, PixelNumber width, const uint8_t data[]) noexcept override final;
+
+ // Draw a bitmap row
+ // x0 = x-coordinate of the top left, measured from left hand edge of the display
+ // y0 = y-coordinate of the top left, measured down from the top of the display
+ // width = width of bitmap in pixels
+ // data = bitmap image, must be ((width + 7)/8) bytes long
+ void BitmapRow(PixelNumber top, PixelNumber left, PixelNumber width, const uint8_t data[], bool invert) noexcept override final;
+
+ // Set the foreground colour. Does nothing on monochrome displays.
+ void SetForegroundColour(Colour col) noexcept override final { }
+
+ // Set the background colour. Does nothing on monochrome displays.
+ void SetBackgroundColour(Colour col) noexcept override final { }
+
+protected:
+ virtual void HardwareInit() noexcept = 0;
+
+ // Start a character at the current row and column, clearing the specified number of space columns
+ void StartCharacter(PixelNumber ySize, PixelNumber numSpaceColumns, PixelNumber numFontColumns) noexcept override final;
+
+ // Write one column of character data at (row, column)
+ void WriteColumnData(PixelNumber ySize, uint32_t columnData) noexcept override final;
+
+ // Finish writing a character
+ void EndCharacter() noexcept override final;
+
+ // Flag a single pixel dirty
+ void SetDirty(PixelNumber r, PixelNumber c) noexcept;
+
+ // Flag a rectangle dirty
+ void SetRectDirty(PixelNumber top, PixelNumber left, PixelNumber bottom, PixelNumber right) noexcept;
+
+ uint8_t *_ecv_array image; // image buffer
+ size_t imageSize;
+ SharedSpiClient device;
+ PixelNumber startRow, startCol, endRow, endCol; // coordinates of the dirty rectangle
+ PixelNumber nextFlushRow; // which row we need to flush next
+ Pin csPin;
+ Pin a0Pin;
+ uint8_t contrastRatio;
+ uint8_t resistorRatio;
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_LCD_MONOLCD_H_ */
diff --git a/src/Display/Lcd/ST7567/Lcd7567.cpp b/src/Display/Lcd/ST7567/Lcd7567.cpp
index a7bbd656..7701fc2c 100644
--- a/src/Display/Lcd/ST7567/Lcd7567.cpp
+++ b/src/Display/Lcd/ST7567/Lcd7567.cpp
@@ -13,12 +13,12 @@ constexpr unsigned int TILE_WIDTH = 8;
constexpr unsigned int TILE_HEIGHT = 8;
Lcd7567::Lcd7567(const LcdFont * const fnts[], size_t nFonts) noexcept
- : Lcd(64, 128, fnts, nFonts, SpiMode::mode3)
+ : MonoLcd(64, 128, fnts, nFonts, SpiMode::mode3)
{
}
// Get the display type
-const char *Lcd7567::GetDisplayTypeName() const noexcept
+const char *_ecv_array Lcd7567::GetDisplayTypeName() const noexcept
{
return "128x64 mono graphics with ST7567 controller";
}
diff --git a/src/Display/Lcd/ST7567/Lcd7567.h b/src/Display/Lcd/ST7567/Lcd7567.h
index ed916063..bd6c3769 100644
--- a/src/Display/Lcd/ST7567/Lcd7567.h
+++ b/src/Display/Lcd/ST7567/Lcd7567.h
@@ -14,9 +14,9 @@
#define ALTERNATIVE_ST7565_FLUSHROW
-#include <Display/Lcd/Lcd.h>
+#include <Display/Lcd/MonoLcd.h>
-class Lcd7567 : public Lcd
+class Lcd7567 : public MonoLcd
{
public:
// Construct a GLCD driver.
@@ -26,7 +26,7 @@ public:
bool FlushSome() noexcept override;
// Get the display type
- const char *GetDisplayTypeName() const noexcept override;
+ const char *_ecv_array GetDisplayTypeName() const noexcept override;
protected:
void HardwareInit() noexcept override;
diff --git a/src/Display/Lcd/ST7920/Lcd7920.cpp b/src/Display/Lcd/ST7920/Lcd7920.cpp
index 71111ac8..fe1a18f5 100644
--- a/src/Display/Lcd/ST7920/Lcd7920.cpp
+++ b/src/Display/Lcd/ST7920/Lcd7920.cpp
@@ -25,12 +25,12 @@ constexpr unsigned int LcdDataDelayMicros = 4; // delay between sending data b
constexpr unsigned int LcdDisplayClearDelayMillis = 3; // 1.6ms should be enough
Lcd7920::Lcd7920(const LcdFont * const fnts[], size_t nFonts) noexcept
- : Lcd(64, 128, fnts, nFonts, SpiMode::mode0)
+ : MonoLcd(64, 128, fnts, nFonts, SpiMode::mode0)
{
}
// Get the display type
-const char *Lcd7920::GetDisplayTypeName() const noexcept
+const char *_ecv_array Lcd7920::GetDisplayTypeName() const noexcept
{
return "128x64 mono graphics with ST7920 controller";
}
@@ -51,7 +51,7 @@ void Lcd7920::HardwareInit() noexcept
CommandDelay();
device.Deselect();
- Clear();
+ ClearAll();
FlushAll();
device.Select();
diff --git a/src/Display/Lcd/ST7920/Lcd7920.h b/src/Display/Lcd/ST7920/Lcd7920.h
index 516e46da..6550c6e7 100644
--- a/src/Display/Lcd/ST7920/Lcd7920.h
+++ b/src/Display/Lcd/ST7920/Lcd7920.h
@@ -5,13 +5,13 @@
#if SUPPORT_12864_LCD
-#include <Display/Lcd/Lcd.h>
+#include <Display/Lcd/MonoLcd.h>
// Class for driving 128x64 graphical LCD fitted with ST7920 controller
// This drives the GLCD in serial mode so that it needs just 2 pins.
// Derive the LCD class from the Print class so that we can print stuff to it in alpha mode
-class Lcd7920 : public Lcd
+class Lcd7920 : public MonoLcd
{
public:
// Construct a GLCD driver.
@@ -21,7 +21,7 @@ public:
bool FlushSome() noexcept override;
// Get the display type
- const char *GetDisplayTypeName() const noexcept override;
+ const char *_ecv_array GetDisplayTypeName() const noexcept override;
protected:
void HardwareInit() noexcept override;
diff --git a/src/Display/Lcd/TFTLcd.cpp b/src/Display/Lcd/TFTLcd.cpp
new file mode 100644
index 00000000..e69ad830
--- /dev/null
+++ b/src/Display/Lcd/TFTLcd.cpp
@@ -0,0 +1,49 @@
+/*
+ * TFTLcd.cpp
+ *
+ * Created on: 6 May 2022
+ * Author: David
+ */
+
+#include "TFTLcd.h"
+
+#if SUPPORT_ILI9488_LCD
+
+#if USE_FONT_CHIP
+TFTLcd::TFTLcd(PixelNumber nr, PixelNumber nc, Pin fontCsPin, SpiMode mode, uint8_t sercomNum) noexcept
+ : Lcd(nr, nc, fontCsPin),
+#else
+TFTLcd::TFTLcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts, SpiMode mode, uint8_t sercomNum) noexcept
+ : Lcd(nr, nc, fnts, nFonts),
+#endif
+ spiDev(sercomNum),
+ fgColour(Colours::White), bgColour(Colours::Blue),
+ spiMode(mode)
+{
+}
+
+TFTLcd::~TFTLcd()
+{
+ pinMode(csPin, INPUT_PULLUP);
+}
+
+// Get the SPI frequency
+uint32_t TFTLcd::GetSpiFrequency() const noexcept
+{
+ return spiFrequency;
+}
+
+// Initialize the display
+void TFTLcd::Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept
+{
+ csPin = p_csPin;
+ csPol = csPolarity;
+ spiFrequency = freq;
+ spiDev.SetClockFrequencyAndMode(freq, spiMode, true); // note we currently always use 9-bit SPI
+ pinMode(csPin, (csPolarity) ? OUTPUT_LOW : OUTPUT_HIGH);
+ HardwareInit();
+}
+
+#endif
+
+// End
diff --git a/src/Display/Lcd/TFTLcd.h b/src/Display/Lcd/TFTLcd.h
new file mode 100644
index 00000000..1b4e635d
--- /dev/null
+++ b/src/Display/Lcd/TFTLcd.h
@@ -0,0 +1,58 @@
+/*
+ * TFTLcd.h
+ *
+ * Created on: 6 May 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_LCD_TFTLCD_H_
+#define SRC_DISPLAY_LCD_TFTLCD_H_
+
+#include "Lcd.h"
+#include <Hardware/Spi/SpiDevice.h>
+
+#if SUPPORT_ILI9488_LCD
+
+// This class represents a TFT LCD that uses an exclusive SPI channel to send data to the screen
+class TFTLcd : public Lcd
+{
+public:
+#if USE_FONT_CHIP
+ TFTLcd(PixelNumber nr, PixelNumber nc, Pin fontCsPin, SpiMode mode, uint8_t sercomNum) noexcept;
+#else
+ TFTLcd(PixelNumber nr, PixelNumber nc, const LcdFont * const fnts[], size_t nFonts, SpiMode mode, uint8_t sercomNum) noexcept;
+#endif
+
+ virtual ~TFTLcd();
+
+ // Get the SPI frequency
+ uint32_t GetSpiFrequency() const noexcept override;
+
+ // Initialize the display
+ void Init(Pin p_csPin, Pin p_a0Pin, bool csPolarity, uint32_t freq, uint8_t p_contrastRatio, uint8_t p_resistorRatio) noexcept override;
+
+ // Flush just some data, returning true if this needs to be called again
+ bool FlushSome() noexcept override { return false; }
+
+ // Set the foreground colour. Does nothing on monochrome displays.
+ void SetForegroundColour(Colour col) noexcept override final { fgColour = col; }
+
+ // Set the background colour. Does nothing on monochrome displays.
+ void SetBackgroundColour(Colour col) noexcept override final { bgColour = col; }
+
+protected:
+ virtual void HardwareInit() noexcept = 0;
+
+ SpiDevice spiDev;
+ Colour fgColour, bgColour;
+ Pin csPin = NoPin;
+ bool csPol;
+
+private:
+ uint32_t spiFrequency = 0;
+ SpiMode spiMode;
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_LCD_TFTLCD_H_ */
diff --git a/src/Display/Menu.cpp b/src/Display/Menu.cpp
index 15f52a46..7722c50e 100644
--- a/src/Display/Menu.cpp
+++ b/src/Display/Menu.cpp
@@ -67,16 +67,23 @@
#include "Menu.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
+#include "TextMenuItem.h"
+#include "ButtonMenuItem.h"
+#include "ValueMenuItem.h"
+#include "FilesMenuItem.h"
+#include "ImageMenuItem.h"
#include "Lcd/Lcd.h"
-#include <Platform/RepRap.h>
-#include <Platform/Platform.h>
#include "Display.h"
+#include "TextMenuItem.h"
+#include "ButtonMenuItem.h"
+#include "ValueMenuItem.h"
+#include "FilesMenuItem.h"
+#include "ImageMenuItem.h"
+
+#include <Platform/RepRap.h>
#include <GCodes/GCodes.h>
-#include <Heating/Heat.h>
-#include <Storage/MassStorage.h>
-#include <Tools/Tool.h>
const uint32_t InactivityTimeout = 20000; // inactivity timeout
const uint32_t ErrorTimeout = 6000; // how long we display an error message for
@@ -113,7 +120,7 @@ void Menu::LoadFixedMenu() noexcept
commandBufferIndex = 0;
rowOffset = 0;
currentMargin = 0;
- lcd.Clear();
+ lcd.ClearAll();
// Instead of Reload():
lcd.SetRightMargin(lcd.GetNumCols() - currentMargin);
@@ -142,10 +149,10 @@ void Menu::DisplayMessageBox(const MessageBox& mbox) noexcept
// Draw and a box and clear the interior
const PixelNumber nr = lcd.GetNumRows(), nc = lcd.GetNumCols();
lcd.SetRightMargin(nc);
- lcd.Line(topBottomMargin, sideMargin, topBottomMargin, nc - sideMargin - 1, PixelMode::PixelSet);
- lcd.Line(topBottomMargin, nc - sideMargin - 1, nr - topBottomMargin - 1, nc - sideMargin - 1, PixelMode::PixelSet);
- lcd.Line(nr - topBottomMargin - 1, sideMargin, nr - topBottomMargin - 1, nc - sideMargin - 1, PixelMode::PixelSet);
- lcd.Line(topBottomMargin, sideMargin, nr - topBottomMargin - 1, sideMargin, PixelMode::PixelSet);
+ lcd.Line(topBottomMargin, sideMargin, topBottomMargin, nc - sideMargin - 1, true);
+ lcd.Line(topBottomMargin, nc - sideMargin - 1, nr - topBottomMargin - 1, nc - sideMargin - 1, true);
+ lcd.Line(nr - topBottomMargin - 1, sideMargin, nr - topBottomMargin - 1, nc - sideMargin - 1, true);
+ lcd.Line(topBottomMargin, sideMargin, nr - topBottomMargin - 1, sideMargin, true);
lcd.Clear(topBottomMargin + 1, sideMargin + 1, nr - topBottomMargin - 1, nc - sideMargin - 1);
// We could draw the static text directly, but it is easier to use the existing classes
@@ -156,33 +163,33 @@ void Menu::DisplayMessageBox(const MessageBox& mbox) noexcept
const PixelNumber left = sideMargin + 1 + insideMargin;
const PixelNumber right = nc - left;
const PixelNumber availableWidth = right - left;
- AddItem(new TextMenuItem(top, left, availableWidth, MenuItem::CentreAlign, fontToUse, MenuItem::AlwaysVisible, mbox.title.c_str()), false);
- AddItem(new TextMenuItem(top + rowHeight, left, availableWidth, MenuItem::CentreAlign, fontToUse, MenuItem::AlwaysVisible, mbox.message.c_str()), false); // only 1 row for now
+ AddItem(new TextMenuItem(top, left, availableWidth, MenuItem::CentreAlign, fontToUse, mbox.GetTitle()), false);
+ AddItem(new TextMenuItem(top + rowHeight, left, availableWidth, MenuItem::CentreAlign, fontToUse, mbox.GetMessage()), false); // only 1 row for now
// Add whichever XYZ jog buttons we have been asked to display - assume only XYZ for now
const PixelNumber axisButtonWidth = availableWidth/4;
const PixelNumber axisButtonStep = (availableWidth - 3 *axisButtonWidth)/2 + axisButtonWidth;
- if (mbox.controls.IsBitSet(X_AXIS))
+ if (mbox.GetControls().IsBitSet(X_AXIS))
{
- AddItem(new ValueMenuItem(top + 2 * rowHeight, left, axisButtonWidth, MenuItem::CentreAlign, fontToUse, MenuItem::AlwaysVisible, true, 510, 1), true);
+ AddItem(new ValueMenuItem(top + 2 * rowHeight, left, axisButtonWidth, MenuItem::CentreAlign, fontToUse, true, nullptr, 510, 1), true);
}
- if (mbox.controls.IsBitSet(Y_AXIS))
+ if (mbox.GetControls().IsBitSet(Y_AXIS))
{
- AddItem(new ValueMenuItem(top + 2 * rowHeight, left + axisButtonStep, axisButtonWidth, MenuItem::CentreAlign, fontToUse, MenuItem::AlwaysVisible, true, 511, 1), true);
+ AddItem(new ValueMenuItem(top + 2 * rowHeight, left + axisButtonStep, axisButtonWidth, MenuItem::CentreAlign, fontToUse, true, nullptr, 511, 1), true);
}
- if (mbox.controls.IsBitSet(Z_AXIS))
+ if (mbox.GetControls().IsBitSet(Z_AXIS))
{
- AddItem(new ValueMenuItem(top + 2 * rowHeight, left + 2 * axisButtonStep, axisButtonWidth, MenuItem::CentreAlign, fontToUse, MenuItem::AlwaysVisible, true, 512, 2), true);
+ AddItem(new ValueMenuItem(top + 2 * rowHeight, left + 2 * axisButtonStep, axisButtonWidth, MenuItem::CentreAlign, fontToUse, true, nullptr, 512, 2), true);
}
const PixelNumber okCancelButtonWidth = 30;
- if (mbox.mode & 2)
+ if (mbox.GetMode() & 2)
{
- AddItem(new ButtonMenuItem(top + 3 * rowHeight, left, okCancelButtonWidth, fontToUse, MenuItem::AlwaysVisible, "OK", "M292 P0", nullptr), true);
+ AddItem(new ButtonMenuItem(top + 3 * rowHeight, left, okCancelButtonWidth, fontToUse, "OK", "M292 P0", nullptr), true);
}
- if (mbox.mode & 1)
+ if (mbox.GetMode() & 1)
{
- AddItem(new ButtonMenuItem(top + 3 * rowHeight, right - okCancelButtonWidth, okCancelButtonWidth, fontToUse, MenuItem::AlwaysVisible, "Cancel", "M292 P1", nullptr), true);
+ AddItem(new ButtonMenuItem(top + 3 * rowHeight, right - okCancelButtonWidth, okCancelButtonWidth, fontToUse, "Cancel", "M292 P1", nullptr), true);
}
}
@@ -204,7 +211,7 @@ void Menu::LoadError(const char *msg, unsigned int line) noexcept
// Remove selectable items that may obscure view of the error message
ResetCache();
- lcd.Clear();
+ lcd.ClearAll();
lcd.SetFont(0);
lcd.printf("Error loading menu\nFile: %s", (numNestedMenus > 0) ? filenames[numNestedMenus - 1].c_str() : "(none)");
if (line != 0)
@@ -235,7 +242,7 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
}
// Find the first word
- char *args = commandWord;
+ char *_ecv_array args = commandWord;
while (isalpha(*args))
{
++args;
@@ -253,9 +260,11 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
}
// Parse the arguments
- MenuItem::Visibility xVis = 0;
+ const char *_ecv_array _ecv_null strVis = nullptr;
+ MenuItem::Visibility xVis = MenuItem::AlwaysVisible;
unsigned int decimals = 0;
unsigned int nparam = 0;
+ const char *_ecv_array _ecv_null strNparam = nullptr;
unsigned int width = 0;
unsigned int alignment = 0;
const char *text = "*";
@@ -273,11 +282,11 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
break;
case 'R':
- row = StrToU32(args, &args);
+ row = StrToU32(args, &args) + rowOffset;
break;
case 'C':
- column = StrToU32(args, &args);
+ column = StrToU32(args, &args) + currentMargin;
break;
case 'F':
@@ -285,7 +294,24 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
break;
case 'V':
- xVis = StrToU32(args, &args);
+ if (*args == '{')
+ {
+ ++args;
+ strVis = args;
+ while (*args != '}' && *args != 0)
+ {
+ ++args;
+ }
+ if (*args == '}')
+ {
+ *args = 0;
+ ++args;
+ }
+ }
+ else
+ {
+ xVis = StrToU32(args, &args);
+ }
break;
case 'D':
@@ -293,7 +319,24 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
break;
case 'N':
- nparam = StrToU32(args, &args);
+ // 'value' command allows the N parameter to be an object mode string
+ if (*args == '{' && StringEqualsIgnoreCase(commandWord, "value"))
+ {
+ strNparam = args;
+ while (*args != '}' && *args != 0)
+ {
+ ++args;
+ }
+ if (*args == '}')
+ {
+ *args = 0;
+ ++args;
+ }
+ }
+ else
+ {
+ nparam = StrToU32(args, &args);
+ }
break;
case 'W':
@@ -336,52 +379,53 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
}
}
- lcd.SetCursor(row + currentMargin, column + currentMargin);
+ MenuItem *_ecv_null newItem = nullptr;
// Create an object resident in memory corresponding to the menu layout file's description
if (StringEqualsIgnoreCase(commandWord, "text"))
{
- const char *const acText = AppendString(text);
- MenuItem * const pNewItem = new TextMenuItem(row, column, width, alignment, fontNumber, xVis, acText);
- AddItem(pNewItem, false);
- column += pNewItem->GetWidth();
+ const char *_ecv_array const acText = AppendString(text);
+ newItem = new TextMenuItem(row, column, width, alignment, fontNumber, acText);
+ AddItem(newItem, false);
+ column += newItem->GetWidth();
}
else if (StringEqualsIgnoreCase(commandWord, "image") && fname != nullptr)
{
- ImageMenuItem * const pNewItem = new ImageMenuItem(row, column, xVis, fname);
- AddItem(pNewItem, false);
- column += pNewItem->GetWidth();
+ newItem = new ImageMenuItem(row, column,fname);
+ AddItem(newItem, false);
+ column += newItem->GetWidth();
}
else if (StringEqualsIgnoreCase(commandWord, "button"))
{
- const char * const textString = AppendString(text);
- const char * const actionString = AppendString(action);
- const char * const c_acFileString = AppendString(fname);
- ButtonMenuItem * const pNewItem = new ButtonMenuItem(row, column, width, fontNumber, xVis, textString, actionString, c_acFileString);
- AddItem(pNewItem, true);
- column += pNewItem->GetWidth();
+ const char *_ecv_array const textString = AppendString(text);
+ const char *_ecv_array const actionString = AppendString(action);
+ const char *_ecv_array const c_acFileString = AppendString(fname);
+ newItem = new ButtonMenuItem(row, column, width, fontNumber, textString, actionString, c_acFileString);
+ AddItem(newItem, true);
+ column += newItem->GetWidth();
}
else if (StringEqualsIgnoreCase(commandWord, "value"))
{
- ValueMenuItem * const pNewItem = new ValueMenuItem(row, column, width, alignment, fontNumber, xVis, false, nparam, decimals);
- AddItem(pNewItem, false);
- column += pNewItem->GetWidth();
+ newItem = new ValueMenuItem(row, column, width, alignment, fontNumber, false, strNparam, nparam, decimals);
+ AddItem(newItem, false);
+ column += newItem->GetWidth();
}
else if (StringEqualsIgnoreCase(commandWord, "alter"))
{
- ValueMenuItem * const pNewItem = new ValueMenuItem(row, column, width, alignment, fontNumber, xVis, true, nparam, decimals);
- AddItem(pNewItem, true);
- column += pNewItem->GetWidth();
+ newItem = new ValueMenuItem(row, column, width, alignment, fontNumber, true, nullptr, nparam, decimals);
+ AddItem(newItem, true);
+ column += newItem->GetWidth();
}
#if HAS_MASS_STORAGE
else if (StringEqualsIgnoreCase(commandWord, "files"))
{
- const char * const actionString = AppendString(action);
- const char *const dir = AppendString(dirpath);
- const char *const acFileString = AppendString(fname);
- AddItem(new FilesMenuItem(row, 0, lcd.GetNumCols(), fontNumber, xVis, actionString, dir, acFileString, nparam), true);
+ const char *_ecv_array const actionString = AppendString(action);
+ const char *_ecv_array const dir = AppendString(dirpath);
+ const char *_ecv_array const acFileString = AppendString(fname);
+ newItem = new FilesMenuItem(row, 0, lcd.GetNumCols(), fontNumber, actionString, dir, acFileString, nparam);
+ AddItem(newItem, true);
row += nparam * lcd.GetFontHeight(fontNumber);
- column = 0;
+ column = currentMargin;
}
#endif
else
@@ -390,6 +434,15 @@ const char *Menu::ParseMenuLine(char * const commandWord) noexcept
return "Unknown command";
}
+ // Deal with the visibility of the newly-added menu item
+ if (strVis != nullptr)
+ {
+ newItem->SetVisibility(strVis);
+ }
+ else
+ {
+ newItem->SetVisibility(xVis);
+ }
return nullptr;
}
@@ -415,23 +468,47 @@ void Menu::ResetCache() noexcept
void Menu::Reload() noexcept
{
displayingFixedMenu = false;
+
+#if 0 // if all menus use the whole screen (no visual nesting)
currentMargin = rowOffset = 0;
- lcd.Clear();
+ lcd.ClearAll();
+#else
+ if (numNestedMenus == 1)
+ {
+ currentMargin = 0;
+ lcd.ClearAll();
+ }
+ else
+ {
+ constexpr PixelNumber indentPerLevel = 10; //TODO make this depend on the screen resolution
+ currentMargin = rowOffset = indentPerLevel * (numNestedMenus - 1);
+ const PixelNumber borderMargin = currentMargin - 2;
+ const PixelNumber right = lcd.GetNumCols() - borderMargin - 1;
+ const PixelNumber bottom = lcd.GetNumRows() - borderMargin - 1;
+ lcd.Clear(borderMargin, borderMargin, bottom, right);
+
+ // Draw the outline
+ lcd.Line(borderMargin, borderMargin, bottom, borderMargin, true);
+ lcd.Line(borderMargin, borderMargin, borderMargin, right, true);
+ lcd.Line(bottom, borderMargin, bottom, right, true);
+ lcd.Line(borderMargin, right, bottom, right, true);
+ }
+#endif
ResetCache();
displayingErrorMessage = false;
lcd.SetRightMargin(lcd.GetNumCols() - currentMargin);
- const char * const fname = filenames[numNestedMenus - 1].c_str();
- FileStore * const file = reprap.GetPlatform().OpenFile(MENU_DIR, fname, OpenMode::read);
+ const char *_ecv_array const fname = filenames[numNestedMenus - 1].c_str();
+ FileStore *_ecv_null const file = reprap.GetPlatform().OpenFile(MENU_DIR, fname, OpenMode::read);
if (file == nullptr)
{
LoadError("File not found", 0);
}
else
{
- row = 0;
- column = 0;
+ row = rowOffset;
+ column = currentMargin;
fontNumber = 0;
commandBufferIndex = 0; // Free the string buffer, which contains layout elements from an old menu
for (unsigned int line = 1; ; ++line)
@@ -553,7 +630,7 @@ void Menu::EncoderActionScrollItemHelper(int action) noexcept
if (rowOffset != tLastOffset)
{
// We have scrolled the whole menu, so redraw it
- lcd.Clear();
+ lcd.ClearAll();
for (MenuItem *item = selectableItems; item != nullptr; item = item->GetNext())
{
item->SetChanged();
@@ -785,6 +862,80 @@ MenuItem *Menu::FindPrevSelectableItem(MenuItem *p) const noexcept
return best;
}
+#if SUPPORT_RESISTIVE_TOUCH
+
+// Search for a selectable item that is close to the touched XY coordinates
+void Menu::HandleTouch(PixelNumber x, PixelNumber y) noexcept
+{
+ constexpr int MaxXerror = 8, MaxYerror = 8; // how far (in pixels) the X and Y coordinates of a touch event need to be to the outline of the item for us to disallow it
+
+ if (displayingErrorMessage)
+ {
+ // Allow the message to be cancelled by a touch
+ TouchBeep();
+ timeoutValue = 1; // cancel the timeout at the next tick
+ }
+ else
+ {
+ int bestError = MaxXerror + MaxYerror;
+ MenuItem *null best = nullptr;
+ for (MenuItem *p = selectableItems; p != nullptr; p = p->GetNext())
+ {
+ if (p->IsVisible())
+ {
+ const int xError = (x < p->GetMinX()) ? p->GetMinX() - x
+ : (x > p->GetMaxX()) ? x - p->GetMaxX()
+ : 0;
+ if (xError < MaxXerror)
+ {
+ const int yError = (y <p->GetMinY()) ? p->GetMinY() - y
+ : (y > p->GetMaxY()) ? y - p->GetMaxY()
+ : 0;
+ if (yError < MaxYerror && xError + yError < bestError)
+ {
+ bestError = xError + yError;
+ best = p;
+ }
+ }
+ }
+ }
+
+ if (best != nullptr)
+ {
+ TouchBeep();
+ if (itemIsSelected) // send the touch to the item itself
+ {
+ if (highlightedItem != nullptr && highlightedItem->IsVisible())
+ {
+ //TODO
+ }
+ else
+ {
+ itemIsSelected = false; // should not get here
+ }
+ }
+ else // click without an item under selection
+ {
+ highlightedItem = best;
+ EncoderActionEnterItemHelper();
+ }
+
+ if (!displayingErrorMessage && !displayingMessageBox) // if the operation did not result in an error and we are not displaying a message box
+ {
+ lastActionTime = millis();
+ timeoutValue = InactivityTimeout;
+ }
+ }
+ }
+}
+
+/*static*/ void Menu::TouchBeep() noexcept
+{
+ reprap.GetDisplay().Beep(TouchBeepFrequency, TouchBeepLength);
+}
+
+#endif
+
#endif
// End
diff --git a/src/Display/Menu.h b/src/Display/Menu.h
index ec83c139..733f04f4 100644
--- a/src/Display/Menu.h
+++ b/src/Display/Menu.h
@@ -10,7 +10,7 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
#include "MenuItem.h"
@@ -32,6 +32,10 @@ public:
void DisplayMessageBox(const MessageBox& mbox) noexcept;
void ClearMessageBox() noexcept;
+#if SUPPORT_RESISTIVE_TOUCH
+ void HandleTouch(PixelNumber x, PixelNumber y) noexcept;
+#endif
+
private:
void LoadFixedMenu() noexcept;
void ResetCache() noexcept;
@@ -54,6 +58,14 @@ private:
static char *SkipWhitespace(char *s) noexcept;
static bool CheckVisibility(MenuItem::Visibility vis) noexcept;
+#if SUPPORT_RESISTIVE_TOUCH
+ static void TouchBeep() noexcept;
+
+ static constexpr uint32_t TouchBeepLength = 20; // beep length in ms
+ static constexpr uint32_t TouchBeepFrequency = 4500; // beep frequency in Hz. Resonant frequency of the piezo sounder is 4.5kHz.
+
+#endif
+
#ifdef __LPC17xx__
static const size_t CommandBufferSize = 1024;
#else
diff --git a/src/Display/MenuItem.cpp b/src/Display/MenuItem.cpp
index ea3d69f5..8c4e2fe0 100644
--- a/src/Display/MenuItem.cpp
+++ b/src/Display/MenuItem.cpp
@@ -7,25 +7,17 @@
#include "MenuItem.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
#include <Platform/RepRap.h>
#include <Heating/Heat.h>
#include <Platform/Platform.h>
#include <GCodes/GCodes.h>
-#include <Movement/Move.h>
-#include "Display.h"
#include <Tools/Tool.h>
#include <PrintMonitor/PrintMonitor.h>
-#ifdef __LPC17xx__
-# include "Network.h"
-#else
-# include <Networking/Network.h>
-#endif
-
-MenuItem::MenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, Visibility vis) noexcept
- : row(r), column(c), width(w), height(0), align(a), fontNumber(fn), visCase(vis), itemChanged(true), highlighted(false), drawn(false), next(nullptr)
+MenuItem::MenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn) noexcept
+ : visStr(nullptr), row(r), column(c), width(w), height(0), align(a), fontNumber(fn), visCase(AlwaysVisible), itemChanged(true), highlighted(false), drawn(false), next(nullptr)
{
}
@@ -76,19 +68,25 @@ void MenuItem::PrintAligned(Lcd& lcd, PixelNumber rightMargin) noexcept
bool MenuItem::IsVisible() const noexcept
{
+ const GCodes& gc = reprap.GetGCodes();
+ if (visStr != nullptr)
+ {
+ return gc.EvaluateConditionForDisplay(visStr);
+ }
+
switch (visCase)
{
default:
case 0: return true;
- case 2: return reprap.GetGCodes().IsReallyPrinting();
- case 3: return !reprap.GetGCodes().IsReallyPrinting();
+ case 2: return gc.IsReallyPrinting();
+ case 3: return !gc.IsReallyPrinting();
case 4: return reprap.GetPrintMonitor().IsPrinting();
case 5: return !reprap.GetPrintMonitor().IsPrinting();
case 6: {
- const PauseState ps = reprap.GetGCodes().GetPauseState();
+ const PauseState ps = gc.GetPauseState();
return ps == PauseState::pausing || ps == PauseState::paused;
}
- case 7: return reprap.GetGCodes().IsReallyPrintingOrResuming();
+ case 7: return gc.IsReallyPrintingOrResuming();
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
case 10: return
# if HAS_MASS_STORAGE
@@ -114,8 +112,9 @@ bool MenuItem::IsVisible() const noexcept
;
#endif
case 20:
- { const auto tool = reprap.GetCurrentOrDefaultTool(); // this can be null, especially during startup
- return tool.IsNotNull() && tool->HasTemperatureFault();
+ {
+ const auto tool = gc.GetPrimaryMovementState().GetLockedCurrentOrDefaultTool(); // this can be null, especially during startup
+ return tool.IsNotNull() && tool->HasTemperatureFault();
}
case 28: return reprap.GetHeat().GetStatus(reprap.GetHeat().GetBedHeater(0)) == HeaterStatus::fault;
}
@@ -131,1028 +130,6 @@ void MenuItem::EraseIfInvisible(Lcd& lcd, PixelNumber tOffset) noexcept
}
}
-TextMenuItem::TextMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, Visibility vis, const char* t) noexcept
- : MenuItem(r, c, w, a, fn, vis), text(t)
-{
-}
-
-void TextMenuItem::CorePrint(Lcd& lcd) noexcept
-{
- lcd.printf("%s", text);
-}
-
-void TextMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
-{
- // We ignore the 'highlight' parameter because text items are not selectable
- if (IsVisible() && (!drawn || itemChanged))
- {
- PrintAligned(lcd, rightMargin);
- itemChanged = false;
- drawn = true;
- }
-}
-
-void TextMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
-{
- if (width == 0)
- {
- lcd.SetFont(fontNumber);
- lcd.SetCursor(lcd.GetNumRows(), 0);
- lcd.SetRightMargin(lcd.GetNumCols());
- lcd.TextInvert(false);
- lcd.printf("%s", text);
- width = lcd.GetColumn();
- if (align == LeftAlign)
- {
- ++width; // add a space column after left-aligned text with no explicit width, so that the next item can follow immediately
- }
- }
- if (height == 0)
- {
- lcd.SetFont(fontNumber);
- height = lcd.GetFontHeight();
- }
-}
-
-ButtonMenuItem::ButtonMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, Visibility vis, const char* t, const char* cmd, char const* acFile) noexcept
- : MenuItem(r, c, w, CentreAlign, fn, vis), text(t), command(cmd), m_acFile(acFile)
-{
-}
-
-void ButtonMenuItem::CorePrint(Lcd& lcd) noexcept
-{
- lcd.WriteSpaces(1); // space at start in case highlighted
- lcd.printf("%s", text);
- lcd.WriteSpaces(1); // space at end to allow for highlighting
-}
-
-void ButtonMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
-{
- if (IsVisible() && (itemChanged || !drawn || highlight != highlighted) && column < lcd.GetNumCols())
- {
- highlighted = highlight;
- PrintAligned(lcd, rightMargin);
- itemChanged = false;
- drawn = true;
- }
-}
-
-void ButtonMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
-{
- if (width == 0)
- {
- lcd.SetFont(fontNumber);
- lcd.SetCursor(lcd.GetNumRows(), 0);
- lcd.SetRightMargin(lcd.GetNumCols());
- lcd.TextInvert(false);
- CorePrint(lcd);
- width = lcd.GetColumn();
- }
- if (height == 0)
- {
- lcd.SetFont(fontNumber);
- height = lcd.GetFontHeight();
- }
-}
-
-// TODO WS1: if we overflow the command or directory string, we should probably offer a return value that tells the caller to do nothing...
-bool ButtonMenuItem::Select(const StringRef& cmd) noexcept
-{
- const int nReplacementIndex = StringContains(command, "#0");
- if (-1 != nReplacementIndex)
- {
- cmd.copy(command, nReplacementIndex);
- cmd.cat(m_acFile);
- }
- else
- {
- cmd.copy(command);
- if (StringEqualsIgnoreCase(command, "menu") && strlen(m_acFile) != 0)
- {
- // For backwards compatibility, if 'menu' is used without any parameters, use the L parameter as the name of the menu file
- cmd.cat(' ');
- cmd.cat(m_acFile);
- }
- }
-
- return true;
-}
-
-PixelNumber ButtonMenuItem::GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept
-{
- PixelNumber tOffsetRequest = tCurrentOffset;
-
- // Are we off the bottom of the visible window?
- if (64 + tCurrentOffset <= row + fontHeight + 1)
- {
- tOffsetRequest = row - 3;
- }
-
- // Should we move back up?
- if (row < tCurrentOffset + 3)
- {
- tOffsetRequest = (row > 3) ? row - 3 : 0;
- }
-
- return tOffsetRequest;
-}
-
-ValueMenuItem::ValueMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, Visibility vis, bool adj, unsigned int v, unsigned int d) noexcept
- : MenuItem(r, c, ((w != 0) ? w : DefaultWidth), a, fn, vis), valIndex(v), currentFormat(PrintFormat::undefined), decimals(d), adjusting(AdjustMode::displaying), adjustable(adj)
-{
-}
-
-void ValueMenuItem::CorePrint(Lcd& lcd) noexcept
-{
- if (adjustable)
- {
- lcd.WriteSpaces(1);
- }
-
- if (error)
- {
- lcd.printf("***");
- }
- else
- {
- switch (currentFormat)
- {
- case PrintFormat::asFloat:
- lcd.printf("%.*f", decimals, (double)currentValue.f);
- break;
-
- case PrintFormat::asPercent:
- lcd.printf("%.*f%%", decimals, (double)currentValue.f);
- break;
-
- case PrintFormat::asUnsigned:
- lcd.printf("%u", currentValue.u);
- break;
-
- case PrintFormat::asSigned:
- lcd.printf("%d", currentValue.i);
- break;
-
- case PrintFormat::asText:
- lcd.printf("%s", textValue);
- break;
-
- case PrintFormat::asIpAddress:
- lcd.printf("%u.%u.%u.%u", currentValue.u & 0x000000FF, (currentValue.u >> 8) & 0x0000000FF, (currentValue.u >> 16) & 0x0000000FF, (currentValue.u >> 24) & 0x0000000FF);
- break;
-
- case PrintFormat::asTime:
- {
- unsigned int hours = currentValue.u/3600,
- minutes = (currentValue.u / 60) % 60,
- seconds = currentValue.u % 60;
- lcd.printf("%u:%02u:%02u", hours, minutes, seconds);
- }
- break;
-
- case PrintFormat::undefined:
- default:
- lcd.printf("***");
- break;
- }
- }
-}
-
-void ValueMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
-{
- if (IsVisible())
- {
- error = false;
- textValue = nullptr;
-
- if (valIndex == 501)
- {
- // Item 501 is a special case because it is text, not a number. We store the current message sequence number in currentValue.
- uint16_t newSeq;
- textValue = reprap.GetLatestMessage(newSeq);
- if (newSeq != currentValue.u)
- {
- itemChanged = true;
- currentValue.u = newSeq;
- currentFormat = PrintFormat::asText;
- }
- }
- else if (adjusting != AdjustMode::adjusting)
- {
- const unsigned int itemNumber = valIndex % 100;
- const Value oldValue = currentValue;
- currentFormat = PrintFormat::asFloat;
-
- switch (valIndex/100)
- {
- case 0: // heater current temperature
- currentValue.f = max<float>(reprap.GetGCodes().GetItemCurrentTemperature(itemNumber), 0.0);
- break;
-
- case 1: // heater active temperature
- currentValue.f = max<float>(reprap.GetGCodes().GetItemActiveTemperature(itemNumber), 0.0);
- break;
-
- case 2: // heater standby temperature
- currentValue.f = max<float>(reprap.GetGCodes().GetItemStandbyTemperature(itemNumber), 0.0);
- break;
-
- case 3: // fan %
- currentValue.f = ((itemNumber == 99)
- ? reprap.GetGCodes().GetMappedFanSpeed()
- : reprap.GetFansManager().GetFanValue(itemNumber)
- ) * 100.0;
- currentFormat = PrintFormat::asPercent;
- break;
-
- case 4: // extruder %
- currentValue.f = reprap.GetGCodes().GetExtrusionFactor(itemNumber) * 100.0;
- currentFormat = PrintFormat::asPercent;
- break;
-
- case 5: // misc
- switch (itemNumber)
- {
- case 0:
- currentValue.f = reprap.GetGCodes().GetSpeedFactor() * 100.0;
- currentFormat = PrintFormat::asPercent;
- break;
-
- // case 1 is the latest message sent by M117, but it handled at the start
-
- case 10: // X
- case 11: // Y
- case 12: // Z
- case 13: // U
- case 14: // V
- case 15: // W
- currentValue.f = reprap.GetGCodes().GetUserCoordinate(itemNumber - 10);
- break;
-
- case 20:
- currentValue.i = reprap.GetCurrentToolNumber();
- currentFormat = PrintFormat::asSigned;
- break;
-
- case 21: // Z baby-step
- currentValue.f = reprap.GetGCodes().GetTotalBabyStepOffset(Z_AXIS);
- break;
-
- // Platform's IP address is the "planned", Network's IP address is the "actual"
- case 30:
- case 31:
- case 32:
- case 33:
- currentValue.u = reprap.GetNetwork().GetIPAddress(0).GetQuad(itemNumber - 30);
- currentFormat = PrintFormat::asUnsigned;
- break;
-
- case 34: // IP address in one go
- currentValue.u = reprap.GetNetwork().GetIPAddress(0).GetV4LittleEndian();
- currentFormat = PrintFormat::asIpAddress;
- break;
-
- case 35: // Percentage of file that has been processed
- currentValue.f = (reprap.GetPrintMonitor().IsPrinting())
- ? reprap.GetPrintMonitor().FractionOfFilePrinted() * 100.0
- : 0;
- currentFormat = PrintFormat::asPercent;
- break;
-
- case 36: // Print time remaining, file-based
- currentValue.u = (reprap.GetPrintMonitor().IsPrinting())
- ? static_cast<int>(reprap.GetPrintMonitor().EstimateTimeLeft(PrintEstimationMethod::fileBased))
- : 0;
- currentFormat = PrintFormat::asTime;
- break;
-
- case 37: // Print time remaining, filament-based
- currentValue.u = (reprap.GetPrintMonitor().IsPrinting())
- ? static_cast<int>(reprap.GetPrintMonitor().EstimateTimeLeft(PrintEstimationMethod::filamentBased))
- : 0;
- currentFormat = PrintFormat::asTime;
- break;
-
- case 38: // requested speed
- currentValue.f = reprap.GetMove().GetRequestedSpeedMmPerSec();
- break;
-
- case 39: // top speed
- currentValue.f = reprap.GetMove().GetTopSpeedMmPerSec();
- break;
-
- default:
- error = true;
- }
- break;
-
- default:
- error = true;
- break;
- }
-
- if (error)
- {
- itemChanged = true;
- }
- else
- {
- switch (currentFormat)
- {
- case PrintFormat::undefined:
- itemChanged = true;
- break;
-
- case PrintFormat::asFloat:
- if (currentValue.f != oldValue.f)
- {
- itemChanged = true;
- }
- break;
-
- case PrintFormat::asSigned:
- if (currentValue.i != oldValue.i)
- {
- itemChanged = true;
- }
- break;
-
- case PrintFormat::asUnsigned:
- case PrintFormat::asIpAddress:
- case PrintFormat::asText:
- case PrintFormat::asTime:
- default:
- if (currentValue.u != oldValue.u)
- {
- itemChanged = true;
- }
- break;
- }
- }
- }
-
- if (itemChanged || !drawn || (highlight != highlighted))
- {
- highlighted = highlight;
- PrintAligned(lcd, rightMargin);
- itemChanged = false;
- drawn = true;
- }
- }
-}
-
-bool ValueMenuItem::Select(const StringRef& cmd) noexcept
-{
- adjusting = AdjustMode::adjusting;
- return false;
-}
-
-void ValueMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
-{
- // The width is always set for a ValueMenuItem so we just need to determine the height
- if (height == 0)
- {
- lcd.SetFont(fontNumber);
- height = lcd.GetFontHeight();
- }
-}
-
-PixelNumber ValueMenuItem::GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept
-{
- // TODO
- return 0;
-}
-
-bool ValueMenuItem::Adjust_SelectHelper() noexcept
-{
- if (adjusting == AdjustMode::adjusting)
- {
- const unsigned int itemNumber = GetReferencedToolNumber();
-
- bool err = false;
- switch (valIndex/100)
- {
- case 1: // heater active temperature
- reprap.GetGCodes().SetItemActiveTemperature(itemNumber, (currentValue.f < 1.0) ? ABS_ZERO : currentValue.f);
- break;
-
- case 2: // heater standby temperature
- reprap.GetGCodes().SetItemStandbyTemperature(itemNumber, (currentValue.f < 1.0) ? ABS_ZERO : currentValue.f);
- break;
-
- case 3: // fan %
- if (itemNumber == 99)
- {
- reprap.GetGCodes().SetMappedFanSpeed(currentValue.f * 0.01);
- }
- else
- {
- reprap.GetFansManager().SetFanValue(itemNumber, currentValue.f * 0.01);
- }
- break;
-
- case 4: // extruder %
- reprap.GetGCodes().SetExtrusionFactor(itemNumber, currentValue.f * 0.01);
- break;
-
- case 5: // misc.
- switch (itemNumber)
- {
- case 0:
- reprap.GetGCodes().SetSpeedFactor(currentValue.f * 0.01);
- break;
-
- case 20:
- reprap.SelectTool(currentValue.i, false);
- break;
-
- case 21: // baby stepping
- break;
-
- default:
- err = true;
- break;
- }
- break;
-
- default:
- err = true;
- break;
- }
-
- if (err)
- {
- reprap.GetDisplay().ErrorBeep();
- }
- }
-
- adjusting = AdjustMode::displaying;
- return true;
-}
-
-unsigned int ValueMenuItem::GetReferencedToolNumber() const noexcept
-{
- unsigned int uToolNumber = valIndex % 100;
- if (79 == uToolNumber)
- {
- uToolNumber = reprap.GetCurrentOrDefaultTool()->Number();
- }
-
- return uToolNumber;
-}
-
-// Adjust the value of this item by 'clicks' click of the encoder. 'clicks' is nonzero.
-// Return true if we have finished adjusting it.
-bool ValueMenuItem::Adjust_AlterHelper(int clicks) noexcept
-{
- itemChanged = true; // we will probably change the value, so it will need to be re-displayed
- const unsigned int itemNumber = GetReferencedToolNumber();
-
- switch (valIndex/100)
- {
- case 1: // heater active temperature
- case 2: // heater standby temperature
- if (itemNumber < 80) // Tool heaters
- {
- // If we're decreasing, make any value smaller than 95 go to 0
- // If we're increasing, make any value between 0 and 95 jump directly to 95
- // Also cap the maximum
- if (0 > clicks) // decrementing
- {
- currentValue.f += (float)clicks;
- if (95.0 > currentValue.f)
- {
- currentValue.f = 0.0;
- }
- }
- else // incrementing
- {
- if (0.0 == currentValue.f)
- {
- currentValue.f = 95.0 - 1.0;
- }
- currentValue.f = min<int>(currentValue.f + (float)clicks, reprap.GetHeat().GetHighestTemperatureLimit(reprap.GetTool(itemNumber)->GetHeater(0)));
- }
- }
- else
- {
- currentValue.f += (float)clicks;
- }
- break;
-
- case 3: // fan %
- currentValue.f = constrain<float>(currentValue.f + (float)clicks, 0.0, 100.0);
- break;
-
- case 5: // misc.
- switch (itemNumber)
- {
- case 0: // 500 Feed Rate
- currentValue.f = constrain<float>(currentValue.f + (float)clicks, 10.0, 500.0);
- break;
-
- case 20: // 520 Tool Selection
- currentValue.i = constrain<int>((int)currentValue.f + clicks, -1, 255);
- break;
-
- case 21: // 521 baby stepping
- {
- String<ShortGCodeLength> cmd;
- cmd.printf("M290 Z%.2f", (double)(0.02 * clicks));
- (void) reprap.GetGCodes().ProcessCommandFromLcd(cmd.c_str());
- adjusting = AdjustMode::liveAdjusting;
- }
- break;
-
- default:
- if (itemNumber >= 10 && itemNumber < 10 + reprap.GetGCodes().GetVisibleAxes()) // 510-518 axis position adjustment
- {
- String<ShortGCodeLength> cmd;
- const float amount = ((itemNumber == 12) ? 0.02 : 0.1) * clicks; // 0.02mm Z resolution, 0.1mm for other axes
- cmd.printf("M120 G91 G1 F3000 %c%.2f M121", 'X' + (itemNumber - 10), (double)amount);
- (void) reprap.GetGCodes().ProcessCommandFromLcd(cmd.c_str());
- adjusting = AdjustMode::liveAdjusting;
- }
- break;
- }
- break;
-
- default:
- currentValue.f += (float)clicks;
- break;
- }
-
- return false;
-}
-
-// Adjust this element, returning true if we have finished adjustment.
-// 'clicks' is the number of encoder clicks to adjust by, or 0 if the button was pushed.
-bool ValueMenuItem::Adjust(int clicks) noexcept
-{
- return (clicks == 0) // if button has been pressed
- ? Adjust_SelectHelper()
- : Adjust_AlterHelper(clicks);
-}
-
-#if HAS_MASS_STORAGE
-FilesMenuItem::FilesMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, Visibility vis, const char *cmd, const char *dir, const char *acFile, unsigned int nf) noexcept
- : MenuItem(r, c, w, LeftAlign, fn, vis), numDisplayLines(nf), command(cmd), initialDirectory(dir), m_acFile(acFile),
- m_uListingFirstVisibleIndex(0), m_uListingSelectedIndex(0)
-{
- // There's no guarantee that initialDirectory has a trailing '/'
- currentDirectory.copy(initialDirectory);
- const size_t len = currentDirectory.strlen();
- if (len == 0 || '/' != currentDirectory[len - 1])
- {
- currentDirectory.cat('/');
- }
-
- initialDirectoryNesting = GetDirectoryNesting();
- sdCardState = notStarted;
-}
-
-void FilesMenuItem::vResetViewState() noexcept
-{
- m_uListingSelectedIndex = 0;
- m_uListingFirstVisibleIndex = 0;
-}
-
-void FilesMenuItem::EnterDirectory() noexcept
-{
- vResetViewState();
-
- m_uHardItemsInDirectory = 0;
- FileInfo oFileInfo;
- if (MassStorage::FindFirst(currentDirectory.c_str(), oFileInfo))
- {
- do
- {
- if (oFileInfo.fileName[0] != '.')
- {
- ++m_uHardItemsInDirectory;
- }
- }
- while (MassStorage::FindNext(oFileInfo));
- }
-
- itemChanged = true; // force a redraw
-}
-
-uint8_t FilesMenuItem::GetDirectoryNesting() const noexcept
-{
- const char *pcPathElement = currentDirectory.c_str();
- uint8_t uNumSlashes = 0;
-
- while ('\0' != *pcPathElement)
- {
- if (('/' == *pcPathElement) && ('\0' != *(pcPathElement + 1))) // don't count a trailing slash
- {
- ++uNumSlashes;
- }
- ++pcPathElement;
- }
- return uNumSlashes;
-}
-
-bool FilesMenuItem::bInSubdirectory() const noexcept
-{
- return GetDirectoryNesting() > initialDirectoryNesting;
-}
-
-unsigned int FilesMenuItem::uListingEntries() const noexcept
-{
- return bInSubdirectory() ? (1 + m_uHardItemsInDirectory) : m_uHardItemsInDirectory;
-}
-
-void FilesMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
-{
- // The 'highlight' parameter is not used to highlight this item, but it is still used to tell whether this item is selected or not
- if (!IsVisible())
- {
- sdCardState = notStarted;
- }
- else if (!drawn || itemChanged || highlighted != highlight)
- {
- switch (sdCardState)
- {
- case notStarted:
- if (MassStorage::CheckDriveMounted(currentDirectory.c_str()))
- {
- sdCardState = mounted;
- EnterDirectory();
- }
- else
- {
- sdCardState = mounting;
- }
- break;
-
- case mounting:
- {
- const size_t card = (isdigit(currentDirectory[0]) && currentDirectory[1] == ':') ? currentDirectory[0] - '0' : 0;
- String<StringLength50> reply;
- switch(MassStorage::Mount(card, reply.GetRef(), false))
- {
- case GCodeResult::notFinished:
- return;
-
- case GCodeResult::ok:
- sdCardState = mounted;
- EnterDirectory();
- break;
-
- default:
- reply.copy("Internal error");
- // no break
- case GCodeResult::error:
- sdCardState = error;
- lcd.SetFont(fontNumber);
- lcd.SetCursor(row, column);
- lcd.SetRightMargin(rightMargin);
- lcd.ClearToMargin();
- lcd.SetCursor(row, column);
- lcd.printf("%s", reply.c_str());
- break;
- }
- }
- break;
-
- case mounted:
- ListFiles(lcd, rightMargin, highlight);
- break;
-
- case error:
- break;
- }
- }
-}
-
-void FilesMenuItem::ListFiles(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
-{
- lcd.SetFont(fontNumber);
- lcd.SetRightMargin(rightMargin);
- uint8_t line = 0;
-
- // If we are in a subdirectory then we prepend ".." to the list of files
- unsigned int dirEntriesToSkip;
- if (bInSubdirectory())
- {
- if (m_uListingFirstVisibleIndex == 0)
- {
- lcd.SetCursor(row, column);
- lcd.printf(" ..");
- lcd.ClearToMargin();
- if (highlight && m_uListingSelectedIndex == 0)
- {
- // Overwriting the initial spaces with '>' avoids shifting the following text when we change the selection
- lcd.SetCursor(row, column);
- lcd.write('>');
- }
- line = 1;
- dirEntriesToSkip = 0;
- }
- else
- {
- dirEntriesToSkip = m_uListingFirstVisibleIndex - 1;
- }
- }
- else
- {
- dirEntriesToSkip = m_uListingFirstVisibleIndex;
- }
-
- // Seek to the first file that is in view
- FileInfo oFileInfo;
- bool gotFileInfo = MassStorage::FindFirst(currentDirectory.c_str(), oFileInfo);
- while (gotFileInfo)
- {
- if (oFileInfo.fileName[0] != '.')
- {
- if (dirEntriesToSkip == 0)
- {
- break;
- }
- --dirEntriesToSkip;
- }
- gotFileInfo = MassStorage::FindNext(oFileInfo);
- }
-
- // We always iterate the entire viewport so that old listing lines that may not be overwritten are cleared
- while (line < numDisplayLines)
- {
- lcd.SetCursor(row + (lcd.GetFontHeight() * line), column);
-
- // If there's actually a file to describe (not just ensuring viewport line clear)
- if (gotFileInfo)
- {
- lcd.printf(" ");
- if (oFileInfo.isDirectory)
- {
- lcd.printf("./");
- }
- lcd.printf("%s", oFileInfo.fileName.c_str());
- lcd.ClearToMargin();
- if (highlight && m_uListingSelectedIndex == line + m_uListingFirstVisibleIndex)
- {
- lcd.SetCursor(row + (lcd.GetFontHeight() * line), column);
- lcd.write('>');
- }
- }
- else
- {
- lcd.ClearToMargin();
- }
-
- ++line;
- if (line == numDisplayLines)
- {
- break; // skip getting more file info for efficiency
- }
-
- do
- {
- gotFileInfo = MassStorage::FindNext(oFileInfo);
- } while (gotFileInfo && oFileInfo.fileName[0] == '.');
- }
-
- MassStorage::AbandonFindNext(); // release the mutex, there may be more files that we don't have room to display
-
- itemChanged = false;
- drawn = true;
- highlighted = highlight;
-}
-
-void FilesMenuItem::Enter(bool bForwardDirection) noexcept
-{
- if (bForwardDirection || uListingEntries() == 0)
- {
- m_uListingSelectedIndex = 0;
- m_uListingFirstVisibleIndex = 0; // select the first item and start the list from the first item
- }
- else
- {
- m_uListingSelectedIndex = uListingEntries() - 1; // select the last item
- m_uListingFirstVisibleIndex = ((uListingEntries() > numDisplayLines) ? (uListingEntries() - numDisplayLines) : 0);
- }
- itemChanged = true;
-}
-
-int FilesMenuItem::Advance(int nCounts) noexcept
-{
- // In case of empty directory, there's nothing the control itself can do
- if (uListingEntries() != 0)
- {
- while (nCounts > 0)
- {
- // Advancing one more would take us past the end of the list
- // Instead, return the remaining count so that the other selectable menu items can be scrolled.
- if (m_uListingSelectedIndex + 1 == uListingEntries())
- {
- break;
- }
-
- ++m_uListingSelectedIndex;
- --nCounts;
-
- // Move the visible portion of the list down, if required
- if (m_uListingSelectedIndex == m_uListingFirstVisibleIndex + numDisplayLines)
- {
- ++m_uListingFirstVisibleIndex;
- }
- }
-
- while (nCounts < 0)
- {
- // We're already at the first item in the visible list; return the remaining action to the menu system.
- if (0 == m_uListingSelectedIndex)
- {
- break;
- }
-
- --m_uListingSelectedIndex;
- ++nCounts;
-
- // Move the visible portion of the list up, if required
- if (m_uListingSelectedIndex < m_uListingFirstVisibleIndex)
- {
- --m_uListingFirstVisibleIndex;
- }
- }
-
- itemChanged = true;
- }
-
- return nCounts;
-}
-
-bool FilesMenuItem::Select(const StringRef& cmd) noexcept
-{
- // Several cases:
- // TEST 1. ".." entry - call EnterDirectory(), using saved state information
- // TEST 2. Directory - call EnterDirectory(), adding to saved state information
- // 3. File - run command with filename as argument
-
- // Get information on the item selected
-
- if (bInSubdirectory() && 0 == m_uListingSelectedIndex) // meaning ".."
- {
- // TODO: go up one level rather than to logical root
- // There's no guarantee that initialDirectory has a trailing '/'
- currentDirectory.copy(initialDirectory);
- const size_t len = currentDirectory.strlen();
- if (len == 0 || '/' != currentDirectory[len - 1])
- {
- currentDirectory.cat('/');
- }
- EnterDirectory();
- }
- else
- {
- // If subdir:
- // If ".." is visible, the selected file is visible index m_uListingSelectedIndex, fs index m_uListingSelectedIndex - 1
- // If ".." is not visible, the selected file is visible index m_uListingSelectedIndex, fs index m_uListingSelectedIndex - 1
- // If logical root:
- // ".." is never visible, so the selected file is visible index m_uListingSelectedIndex, fs index m_uListingSelectedIndex
- unsigned int dirEntriesToSkip = bInSubdirectory() ? m_uListingSelectedIndex - 1 : m_uListingSelectedIndex;
-
- // Seek to the selected file
- FileInfo oFileInfo;
- bool gotFileInfo = MassStorage::FindFirst(currentDirectory.c_str(), oFileInfo);
- while (gotFileInfo)
- {
- if (oFileInfo.fileName[0] != '.')
- {
- if (dirEntriesToSkip == 0)
- {
- break;
- }
- --dirEntriesToSkip;
- }
- gotFileInfo = MassStorage::FindNext(oFileInfo);
- }
- MassStorage::AbandonFindNext();
-
- if (gotFileInfo) // handles empty directory (no action)
- {
- if (oFileInfo.isDirectory)
- {
- // Build the new directory, and ensure it's terminated with a forward slash
- currentDirectory.cat(oFileInfo.fileName.c_str());
- currentDirectory.cat('/');
- EnterDirectory();
- }
- else
- {
- int nReplacementIndex = StringContains(command, "#0");
- if (nReplacementIndex != -1)
- {
- cmd.copy(command, nReplacementIndex);
- cmd.cat('"');
- cmd.cat(currentDirectory.c_str());
- cmd.cat(oFileInfo.fileName.c_str());
- cmd.cat('"');
- cmd.cat(command + nReplacementIndex + strlen("#0"));
- }
- else
- {
- cmd.copy(command);
- }
-
- // TODO: do this on the way in and it might be less work...
- // On the other hand, this only occurs when an item is selected so it's O(1) vs. O(n)
- nReplacementIndex = cmd.Contains("menu");
- if (nReplacementIndex != -1)
- {
- cmd.Truncate(nReplacementIndex);
- cmd.cat(m_acFile);
- }
-
- return true;
- }
- }
- }
-
- return false;
-}
-
-void FilesMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
-{
- // The width is always set for a FilesMenuItem so we just need to determine the height
- if (height == 0)
- {
- lcd.SetFont(fontNumber);
- height = lcd.GetFontHeight() * numDisplayLines;
- }
-}
-
-PixelNumber FilesMenuItem::GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept
-{
- // TODO
- return 0;
-}
-#endif
-
-// Image menu item members
-// The image file format is:
-// Byte 0 = number of columns
-// Byte 1 = number of rows
-// Remaining bytes = data, 1 row at a time. If the number of columns is not a multiple of 8 then the data for each row is padded to a multiple of 8 bits.
-ImageMenuItem::ImageMenuItem(PixelNumber r, PixelNumber c, Visibility vis, const char *pFileName) noexcept
- : MenuItem(r, c, 0, 0, 0, vis)
-{
- fileName.copy(pFileName);
-}
-
-void ImageMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
-{
- if (IsVisible() && (!drawn || itemChanged || highlight != highlighted))
- {
- FileStore * const fs = reprap.GetPlatform().OpenFile(MENU_DIR, fileName.c_str(), OpenMode::read);
- if (fs != nullptr)
- {
- uint8_t widthAndHeight[2];
- if (fs->Read(widthAndHeight, 2) == 2)
- {
- const PixelNumber cols = widthAndHeight[0];
- const PixelNumber rows = widthAndHeight[1];
- if (cols != 0 && cols <= lcd.GetNumCols() && rows != 0)
- {
- const size_t bytesPerRow = (cols + 7)/8;
- for (PixelNumber irow = 0; irow < rows; ++irow)
- {
- uint8_t buffer[lcd.GetNumCols()/8];
- if (fs->Read(buffer, bytesPerRow) != (int)bytesPerRow)
- {
- break;
- }
- lcd.BitmapRow(row + irow, column, cols, buffer, highlight);
- }
- }
- }
- fs->Close();
- }
- itemChanged = false;
- drawn = true;
- highlighted = highlight;
- }
-}
-
-void ImageMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
-{
- if (width == 0 || height == 0)
- {
- FileStore * const fs = reprap.GetPlatform().OpenFile(MENU_DIR, fileName.c_str(), OpenMode::read);
- if (fs != nullptr)
- {
- uint8_t w[2];
- fs->Read(w, 2); // read the number of columns
- fs->Close();
- width = w[0];
- height = w[1];
- }
- }
-}
-
#endif
// End
diff --git a/src/Display/MenuItem.h b/src/Display/MenuItem.h
index 260a0c6c..1ca26e6f 100644
--- a/src/Display/MenuItem.h
+++ b/src/Display/MenuItem.h
@@ -10,7 +10,7 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
#include "General/FreelistManager.h"
#include "Lcd/Lcd.h"
@@ -57,9 +57,14 @@ public:
virtual ~MenuItem() noexcept { }
- MenuItem *GetNext() const noexcept { return next; }
+ MenuItem *null GetNext() const noexcept { return next; }
FontNumber GetFontNumber() const noexcept { return fontNumber; }
void SetChanged() noexcept { itemChanged = true; }
+
+ // The following functions to set the visibility do not update the display. They should only be called immediately after creating the item and before displaying it for the first time.
+ void SetVisibility(Visibility vis) noexcept { visCase = vis; }
+ void SetVisibility(const char *_ecv_array vis) noexcept { visStr = vis; }
+
bool IsVisible() const noexcept;
// Erase this item if it is drawn but should not be visible
@@ -69,10 +74,15 @@ public:
PixelNumber GetWidth() const noexcept { return width; }
PixelNumber GetHeight() const noexcept { return height; }
+ PixelNumber GetMinX() const noexcept { return column; }
+ PixelNumber GetMinY() const noexcept { return row; }
+ PixelNumber GetMaxX() const noexcept { return column + width - 1; }
+ PixelNumber GetMaxY() const noexcept { return row + height - 1; }
+
static void AppendToList(MenuItem **root, MenuItem *item) noexcept;
protected:
- MenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, Visibility v) noexcept;
+ MenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn) noexcept;
// Print the item starting at the current cursor position, which may be off screen. Used to find the width and also to really print the item.
// Overridden for items that support variable alignment
@@ -81,170 +91,21 @@ protected:
// Print the item at the correct place with the correct alignment
void PrintAligned(Lcd& lcd, PixelNumber rightMargin) noexcept;
+ const char *_ecv_array _ecv_null visStr;
const PixelNumber row, column;
PixelNumber width, height;
const Alignment align;
const FontNumber fontNumber;
- const Visibility visCase;
+ Visibility visCase;
- bool itemChanged;
- bool highlighted;
- bool drawn;
+ uint8_t itemChanged : 1,
+ highlighted : 1,
+ drawn : 1;
private:
MenuItem *next;
};
-class TextMenuItem final : public MenuItem
-{
-public:
- void* operator new(size_t sz) noexcept { return FreelistManager::Allocate<TextMenuItem>(); }
- void operator delete(void* p) noexcept { FreelistManager::Release<TextMenuItem>(p); }
-
- TextMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, Visibility vis, const char *t) noexcept;
- void Draw(Lcd& lcd, PixelNumber maxWidth, bool highlight) noexcept override;
- void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
-
-protected:
- void CorePrint(Lcd& lcd) noexcept override;
-
-private:
- const char *text;
-};
-
-class ButtonMenuItem final : public MenuItem
-{
-public:
- void* operator new(size_t sz) noexcept { return FreelistManager::Allocate<ButtonMenuItem>(); }
- void operator delete(void* p) noexcept { FreelistManager::Release<ButtonMenuItem>(p); }
-
- ButtonMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, Visibility vis, const char *t, const char *cmd, const char *acFile) noexcept;
- void Draw(Lcd& lcd, PixelNumber maxWidth, bool highlight) noexcept override;
- void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
- bool Select(const StringRef& cmd) noexcept override;
-
- PixelNumber GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept override;
-
-protected:
- void CorePrint(Lcd& lcd) noexcept override;
-
-private:
- const char *text;
- const char *command;
- const char *m_acFile; // used when action ("command") is "menu"
-};
-
-class ValueMenuItem final : public MenuItem
-{
-public:
- void* operator new(size_t sz) noexcept { return FreelistManager::Allocate<ValueMenuItem>(); }
- void operator delete(void* p) noexcept { FreelistManager::Release<ValueMenuItem>(p); }
-
- ValueMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, Visibility vis, bool adj, unsigned int v, unsigned int d) noexcept;
- void Draw(Lcd& lcd, PixelNumber maxWidth, bool highlight) noexcept override;
- bool Select(const StringRef& cmd) noexcept override;
- bool CanAdjust() const noexcept override { return true; }
- bool Adjust(int clicks) noexcept override;
- void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
-
- PixelNumber GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept override;
-
- unsigned int GetReferencedToolNumber() const noexcept;
-
-protected:
- void CorePrint(Lcd& lcd) noexcept override;
-
-private:
- enum class AdjustMode : uint8_t { displaying, adjusting, liveAdjusting };
- enum class PrintFormat : uint8_t { undefined, asFloat, asUnsigned, asSigned, asPercent, asText, asIpAddress, asTime };
-
- bool Adjust_SelectHelper() noexcept;
- bool Adjust_AlterHelper(int clicks) noexcept;
-
- static constexpr PixelNumber DefaultWidth = 25; // default numeric field width
-
- const unsigned int valIndex;
- const char *textValue; // for temporary use when printing
-
- // Variables currentValue, currentFormat and decimals together define the display format of the item
- union Value
- { float f;
- uint32_t u;
- int32_t i;
- };
-
- Value currentValue;
- PrintFormat currentFormat;
- uint8_t decimals;
- AdjustMode adjusting;
- bool adjustable;
- bool error; // for temporary use when printing
-};
-
-#if HAS_MASS_STORAGE
-class FilesMenuItem final : public MenuItem
-{
-public:
- void* operator new(size_t sz) noexcept { return FreelistManager::Allocate<FilesMenuItem>(); }
- void operator delete(void* p) noexcept { FreelistManager::Release<FilesMenuItem>(p); }
-
- FilesMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, FontNumber fn, Visibility vis, const char *cmd, const char *dir, const char *acFile, unsigned int nf) noexcept;
- void Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept override;
- void Enter(bool bForwardDirection) noexcept override;
- int Advance(int nCounts) noexcept override;
- bool Select(const StringRef& cmd) noexcept override;
- void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
-
- PixelNumber GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept override;
-
- void EnterDirectory() noexcept;
-
-protected:
- void vResetViewState() noexcept;
-
-private:
- void ListFiles(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept;
- uint8_t GetDirectoryNesting() const noexcept;
-
- const unsigned int numDisplayLines;
-
- const char *command;
- const char *initialDirectory;
- const char *m_acFile; // used when action ("command") includes "menu"
-
- // Working
- String<MaxFilenameLength> currentDirectory;
-
- bool bInSubdirectory() const noexcept;
- unsigned int uListingEntries() const noexcept;
-
- // Files on the file system, real count i.e. no ".." included
- unsigned int m_uHardItemsInDirectory;
-
- // Logical items (c. files) for display, referenced to uListingEntries() count
- unsigned int m_uListingFirstVisibleIndex;
- unsigned int m_uListingSelectedIndex;
-
- enum CardState : uint8_t { notStarted, mounting, mounted, error } sdCardState;
- uint8_t initialDirectoryNesting;
-};
-#endif
-
-class ImageMenuItem final : public MenuItem
-{
-public:
- void* operator new(size_t sz) noexcept { return FreelistManager::Allocate<ImageMenuItem>(); }
- void operator delete(void* p) noexcept { FreelistManager::Release<ImageMenuItem>(p); }
-
- ImageMenuItem(PixelNumber r, PixelNumber c, Visibility vis, const char *pFileName) noexcept;
-
- void Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept override;
- void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
-
-private:
- String<MaxFilenameLength> fileName;
-};
-
#endif
#endif /* SRC_DISPLAY_MENUITEM_H_ */
diff --git a/src/Display/ResistiveTouch.cpp b/src/Display/ResistiveTouch.cpp
new file mode 100644
index 00000000..777eba3b
--- /dev/null
+++ b/src/Display/ResistiveTouch.cpp
@@ -0,0 +1,182 @@
+/*
+ Resistive touch screen support
+ Based using the approach described in TI app note http://www.ti.com/lit/pdf/sbaa036.
+*/
+
+#include "ResistiveTouch.h"
+
+#if SUPPORT_RESISTIVE_TOUCH
+
+#include <Hardware/Spi/SharedSpiDevice.h>
+
+ResistiveTouch::ResistiveTouch(Pin csp, Pin irqp) noexcept
+ : spiDev(SharedSpiDevice::GetMainSharedSpiDevice(), SpiFrequency, SpiMode::mode0, csp, false),
+ csPin(csp), irqPin(irqp)
+{
+ pinMode(csPin, OUTPUT_HIGH);
+ pinMode(irqPin, INPUT_PULLUP);
+}
+
+void ResistiveTouch::Init(uint16_t xp, uint16_t yp, DisplayOrientation orientationAdjust) noexcept
+{
+ orientAdjust = orientationAdjust;
+ disp_x_size = xp;
+ disp_y_size = yp;
+ offsetX = 0;
+ scaleX = (uint16_t)(((uint32_t)(disp_x_size - 1) << 16)/4095);
+ offsetY = 0;
+ scaleY = (uint16_t)(((uint32_t)(disp_y_size - 1) << 16)/4095);
+
+ pressed = false;
+}
+
+// If the panel is touched, return the coordinates in x and y and return true; else return false
+bool ResistiveTouch::Read(uint16_t &px, uint16_t &py, bool &repeat, uint16_t *null rawX, uint16_t *null rawY) noexcept
+{
+ bool ret = false;
+
+ repeat = false;
+
+ if (!digitalRead(irqPin)) // if screen is touched
+ {
+ spiDev.Select();
+ delayMicroseconds(100); // allow the screen to settle
+ uint16_t tx;
+ if (GetData(false, tx))
+ {
+ uint16_t ty;
+ if (GetData(true, ty))
+ {
+ if (!digitalRead(irqPin))
+ {
+ int16_t valx = (orientAdjust & SwapXY) ? ty : tx;
+ if (orientAdjust & ReverseX)
+ {
+ valx = 4095 - valx;
+ }
+
+ int16_t cx = (int16_t)(((uint32_t)valx * (uint32_t)scaleX) >> 16) - offsetX;
+ px = (cx < 0) ? 0 : (cx >= disp_x_size) ? disp_x_size - 1 : (uint16_t)cx;
+
+ int16_t valy = (orientAdjust & SwapXY) ? tx : ty;
+ if (orientAdjust & ReverseY)
+ {
+ valy = 4095 - valy;
+ }
+
+ int16_t cy = (int16_t)(((uint32_t)valy * (uint32_t)scaleY) >> 16) - offsetY;
+ py = (cy < 0) ? 0 : (cy >= disp_y_size) ? disp_y_size - 1 : (uint16_t)cy;
+ if (rawX != nullptr)
+ {
+ *rawX = valx;
+ }
+ if (rawY != nullptr)
+ {
+ *rawY = valy;
+ }
+ ret = true;
+ }
+ }
+ }
+ spiDev.Deselect();
+ }
+
+
+ if (ret && pressed)
+ {
+ repeat = true;
+ }
+
+ pressed = ret;
+ return ret;
+}
+
+// Get data from the touch chip. CS has already been set low.
+// We need to allow the touch chip ADC input to settle. See TI app note http://www.ti.com/lit/pdf/sbaa036.
+bool ResistiveTouch::GetData(bool wantY, uint16_t &rslt) noexcept
+{
+ uint8_t command = (wantY) ? 0xD3 : 0x93; // start, channel 5 (y) or 1 (x), 12-bit, differential mode, don't power down between conversions
+ WriteCommand(command); // send the command
+ ReadData(command); // discard the first result and send the same command again
+
+ const size_t NumReadings = 8;
+ const uint16_t MaxDiff = 40; // needs to be big enough to handle jitter.
+ // 8 was OK for the 4.3 and 5 inch displays but not the 7 inch.
+ // 25 is OK for most 7" displays.
+ const unsigned int MaxAttempts = 16;
+
+ uint16_t ring[NumReadings];
+ uint32_t sum = 0;
+
+ // Take enough readings to fill the ring buffer
+ for (size_t i = 0; i < NumReadings; ++i)
+ {
+ const uint16_t val = ReadData(command);
+ ring[i] = val;
+ sum += val;
+ }
+
+ // Test whether every reading is within 'maxDiff' of the average reading.
+ // If it is, return the average reading.
+ // If not, take another reading and try again, up to 'maxAttempts' times.
+ uint16_t avg;
+ size_t last = 0;
+ bool ok;
+ for (unsigned int k = 0; k < MaxAttempts; ++k)
+ {
+ avg = (uint16_t)(sum/NumReadings);
+ ok = true;
+ for (size_t i = 0; ok && i < NumReadings; ++i)
+ {
+ if (Diff(avg, ring[i]) > MaxDiff)
+ {
+ ok = false;
+ break;
+ }
+ }
+ if (ok)
+ {
+ break;
+ }
+
+ // Take another reading
+ sum -= ring[last];
+ uint16_t val = ReadData(command);
+ ring[last] = val;
+ sum += val;
+ last = (last + 1) % NumReadings;
+ }
+
+ ReadData(command & 0xF8); // tell it to power down between conversions
+ ReadData(0); // read the final data
+ rslt = avg;
+ return ok;
+}
+
+// Send the first command in a chain. The chip latches the data bit on the rising edge of the clock. We have already set CS low.
+void ResistiveTouch::WriteCommand(uint8_t command) noexcept
+{
+ spiDev.WritePacket(&command, 1);
+}
+
+// Read the data, and write another command at the same time. We have already set CS low.
+// The chip produces its data bit after the falling edge of the clock. After sending 8 clocks, we can send a command again.
+uint16_t ResistiveTouch::ReadData(uint8_t command) noexcept
+{
+ uint16_t cmd = (uint16_t)command;
+ uint16_t data = 0;
+ spiDev.TransceivePacket((const uint8_t *_ecv_array)&cmd, (uint8_t *_ecv_array)&data, 2);
+ return data >> 3;
+}
+
+void ResistiveTouch::Calibrate(uint16_t xlow, uint16_t xhigh, uint16_t ylow, uint16_t yhigh, uint16_t margin) noexcept
+{
+ scaleX = (uint16_t)(((uint32_t)(disp_x_size - 1 - 2 * margin) << 16)/(xhigh - xlow));
+ offsetX = (int16_t)(((uint32_t)xlow * (uint32_t)scaleX) >> 16) - (int16_t)margin;
+ scaleY = (uint16_t)(((uint32_t)(disp_y_size - 1 - 2 * margin) << 16)/(yhigh - ylow));
+ offsetY = (int16_t)(((uint32_t)ylow * (uint32_t)scaleY) >> 16) - (int16_t)margin;
+}
+
+#endif
+
+// End
diff --git a/src/Display/ResistiveTouch.h b/src/Display/ResistiveTouch.h
new file mode 100644
index 00000000..396360c6
--- /dev/null
+++ b/src/Display/ResistiveTouch.h
@@ -0,0 +1,46 @@
+/*
+ UTouch.cpp - library support for Color TFT LCD Touch screens on SAM3X
+ Originally based on Utouch library by Henning Karlsen.
+ Rewritten by D Crocker using the approach described in TI app note http://www.ti.com/lit/pdf/sbaa036.
+*/
+
+#ifndef SRC_DISPLAY_RESISTIVE_TOUCH_H
+#define SRC_DISPLAY_RESISTIVE_TOUCH_H
+
+#include <RepRapFirmware.h>
+
+#if SUPPORT_RESISTIVE_TOUCH
+
+#include "DisplayOrientation.h"
+#include <Hardware/Spi/SharedSpiClient.h>
+
+class ResistiveTouch
+{
+public:
+ ResistiveTouch(Pin csp, Pin irqp) noexcept;
+
+ void Init(uint16_t xp, uint16_t yp, DisplayOrientation orientationAdjust) noexcept;
+ bool Read(uint16_t &x, uint16_t &y, bool &repeat, uint16_t* null rawX = nullptr, uint16_t *null rawY = nullptr) noexcept;
+ void Calibrate(uint16_t xlow, uint16_t xhigh, uint16_t ylow, uint16_t yhigh, uint16_t margin) noexcept;
+
+private:
+ SharedSpiClient spiDev;
+ Pin csPin, irqPin;
+ uint16_t disp_x_size, disp_y_size;
+ uint16_t scaleX, scaleY;
+ int16_t offsetX, offsetY;
+
+ DisplayOrientation orientAdjust;
+ bool pressed;
+
+ bool GetData(bool wantY, uint16_t &rslt) noexcept;
+ void WriteCommand(uint8_t command) noexcept;
+ uint16_t ReadData(uint8_t command) noexcept;
+ static uint16_t Diff(uint16_t a, uint16_t b) noexcept { return (a < b) ? b - a : a - b; }
+
+ static constexpr uint32_t SpiFrequency = 2000000; // max supported by the XPT2046 is 2.5MHz
+};
+
+#endif
+
+#endif // SRC_DISPLAY_RESISTIVE_TOUCH_H
diff --git a/src/Display/RotaryEncoder.cpp b/src/Display/RotaryEncoder.cpp
index 4b08aa15..5f4ba58b 100644
--- a/src/Display/RotaryEncoder.cpp
+++ b/src/Display/RotaryEncoder.cpp
@@ -1,6 +1,6 @@
#include "RotaryEncoder.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_ROTARY_ENCODER
#include <Hardware/IoPorts.h>
diff --git a/src/Display/RotaryEncoder.h b/src/Display/RotaryEncoder.h
index e3df5d4c..5cecdd84 100644
--- a/src/Display/RotaryEncoder.h
+++ b/src/Display/RotaryEncoder.h
@@ -3,7 +3,7 @@
#include "RepRapFirmware.h"
-#if SUPPORT_12864_LCD
+#if SUPPORT_ROTARY_ENCODER
// Class to manage a rotary encoder with a push button
class RotaryEncoder
diff --git a/src/Display/TextMenuItem.cpp b/src/Display/TextMenuItem.cpp
new file mode 100644
index 00000000..a6d39f56
--- /dev/null
+++ b/src/Display/TextMenuItem.cpp
@@ -0,0 +1,57 @@
+/*
+ * TextMenuItem.cpp
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#include "TextMenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+TextMenuItem::TextMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, const char *_ecv_array t) noexcept
+ : MenuItem(r, c, w, a, fn), text(t)
+{
+}
+
+void TextMenuItem::CorePrint(Lcd& lcd) noexcept
+{
+ lcd.printf("%s", text);
+}
+
+void TextMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
+{
+ // We ignore the 'highlight' parameter because text items are not selectable
+ if (IsVisible() && (!drawn || itemChanged))
+ {
+ PrintAligned(lcd, rightMargin);
+ itemChanged = false;
+ drawn = true;
+ }
+}
+
+void TextMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
+{
+ if (width == 0)
+ {
+ lcd.SetFont(fontNumber);
+ lcd.SetCursor(lcd.GetNumRows(), 0);
+ lcd.SetRightMargin(lcd.GetNumCols());
+ lcd.TextInvert(false);
+ lcd.printf("%s", text);
+ width = lcd.GetColumn();
+ if (align == LeftAlign)
+ {
+ ++width; // add a space column after left-aligned text with no explicit width, so that the next item can follow immediately
+ }
+ }
+ if (height == 0)
+ {
+ lcd.SetFont(fontNumber);
+ height = lcd.GetFontHeight();
+ }
+}
+
+#endif
+
+// End
diff --git a/src/Display/TextMenuItem.h b/src/Display/TextMenuItem.h
new file mode 100644
index 00000000..2da2492b
--- /dev/null
+++ b/src/Display/TextMenuItem.h
@@ -0,0 +1,33 @@
+/*
+ * TextMenuItem.h
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_TEXTMENUITEM_H_
+#define SRC_DISPLAY_TEXTMENUITEM_H_
+
+#include "MenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+class TextMenuItem final : public MenuItem
+{
+public:
+ DECLARE_FREELIST_NEW_DELETE(TextMenuItem)
+
+ TextMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, const char *_ecv_array t) noexcept;
+ void Draw(Lcd& lcd, PixelNumber maxWidth, bool highlight) noexcept override;
+ void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
+
+protected:
+ void CorePrint(Lcd& lcd) noexcept override;
+
+private:
+ const char *_ecv_array text;
+};
+
+#endif
+
+#endif /* SRC_DISPLAY_TEXTMENUITEM_H_ */
diff --git a/src/Display/ValueMenuItem.cpp b/src/Display/ValueMenuItem.cpp
new file mode 100644
index 00000000..9843fe45
--- /dev/null
+++ b/src/Display/ValueMenuItem.cpp
@@ -0,0 +1,399 @@
+/*
+ * ValueMenuItem.cpp
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#include "ValueMenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+#include <Platform/RepRap.h>
+#include <Display/Display.h>
+#include <GCodes/GCodes.h>
+#include <Heating/Heat.h>
+#include <Movement/Move.h>
+#include <Networking/Network.h>
+#include <PrintMonitor/PrintMonitor.h>
+#include <Tools/Tool.h>
+
+ValueMenuItem::ValueMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, bool adj, const char *_ecv_array _ecv_null om, unsigned int v, unsigned int d) noexcept
+ : MenuItem(r, c, ((w != 0) ? w : DefaultWidth), a, fn), omText(om), valIndex(v), decimals(d), adjustable(adj), adjusting(AdjustMode::displaying), error(false), asPercent(false)
+{
+}
+
+void ValueMenuItem::CorePrint(Lcd& lcd) noexcept
+{
+ if (adjustable)
+ {
+ lcd.WriteSpaces(1);
+ }
+
+ if (error)
+ {
+ lcd.printf("***");
+ }
+ else if (textValue != nullptr)
+ {
+ lcd.printf("%s", textValue);
+ }
+ else if (currentValue.GetType() == TypeCode::Float)
+ {
+ // We handle floats ourselves so that we can handle decimal places and percentages
+ lcd.printf((asPercent) ? "%.*f%%" : "%.*f", decimals, (double)currentValue.fVal);
+ }
+ else
+ {
+ String<StringLength50> str;
+ currentValue.AppendAsString(str.GetRef());
+ lcd.printf("%s", str.c_str());
+ }
+}
+
+void ValueMenuItem::Draw(Lcd& lcd, PixelNumber rightMargin, bool highlight) noexcept
+{
+ if (IsVisible())
+ {
+ error = asPercent = false;
+ textValue = nullptr;
+
+ if (omText != nullptr && valIndex == 501)
+ {
+ // Item 501 is a special case because it is text, not a number. We store the current message sequence number in currentValue.
+ uint16_t newSeq;
+ textValue = reprap.GetLatestMessage(newSeq);
+ if (newSeq != currentValue.uVal)
+ {
+ itemChanged = true;
+ currentValue.uVal = newSeq;
+ }
+ }
+ else
+ {
+ const ExpressionValue oldValue = currentValue;
+ if (omText != nullptr)
+ {
+ error = reprap.GetGCodes().EvaluateValueForDisplay(omText, currentValue);
+ }
+ else if (adjusting != AdjustMode::adjusting)
+ {
+ // Fetch the value and display it
+ const unsigned int itemNumber = valIndex % 100;
+
+ switch (valIndex/100)
+ {
+ case 0: // heater current temperature
+ currentValue.SetFloat(max<float>(reprap.GetGCodes().GetItemCurrentTemperature(itemNumber), 0.0));
+ break;
+
+ case 1: // heater active temperature
+ currentValue.SetFloat(max<float>(reprap.GetGCodes().GetItemActiveTemperature(itemNumber), 0.0));
+ break;
+
+ case 2: // heater standby temperature
+ currentValue.SetFloat(max<float>(reprap.GetGCodes().GetItemStandbyTemperature(itemNumber), 0.0));
+ break;
+
+ case 3: // fan %
+ {
+ const float val = ((itemNumber == 99)
+ ? reprap.GetGCodes().GetPrimaryMovementState().virtualFanSpeed
+ : reprap.GetFansManager().GetFanValue(itemNumber)
+ ) * 100.0;
+ currentValue.SetFloat(val);
+ asPercent = true;
+ }
+ break;
+
+ case 4: // extruder %
+ currentValue.SetFloat(reprap.GetGCodes().GetExtrusionFactor(itemNumber) * 100.0f);
+ asPercent = true;
+ break;
+
+ case 5: // misc
+ switch (itemNumber)
+ {
+ case 0:
+ currentValue.SetFloat(reprap.GetGCodes().GetPrimarySpeedFactor() * 100.0f);
+ asPercent = true;
+ break;
+
+ // case 1 is the latest message sent by M117, but it handled at the start
+
+ case 10: // X
+ case 11: // Y
+ case 12: // Z
+ case 13: // U
+ case 14: // V
+ case 15: // W
+ currentValue.SetFloat(reprap.GetGCodes().GetUserCoordinate(reprap.GetGCodes().GetPrimaryMovementState(), itemNumber - 10));
+ break;
+
+ case 20:
+ currentValue.SetInt(reprap.GetGCodes().GetPrimaryMovementState().GetCurrentToolNumber());
+ break;
+
+ case 21: // Z baby-step
+ currentValue.SetFloat(reprap.GetGCodes().GetTotalBabyStepOffset(Z_AXIS));
+ break;
+
+ // Platform's IP address is the "planned", Network's IP address is the "actual"
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ currentValue.SetUnsigned(reprap.GetNetwork().GetIPAddress(0).GetQuad(itemNumber - 30));
+ break;
+
+ case 34: // IP address in one go
+ currentValue.SetIPAddress(reprap.GetNetwork().GetIPAddress(0));
+ break;
+
+ case 35: // Percentage of file that has been processed
+ currentValue.SetFloat((reprap.GetPrintMonitor().IsPrinting())
+ ? reprap.GetPrintMonitor().FractionOfFilePrinted() * 100.0f
+ : 0.0f);
+ asPercent = true;
+ break;
+
+ case 36: // Print time remaining, file-based
+ currentValue.SetDuration((reprap.GetPrintMonitor().IsPrinting())
+ ? static_cast<int>(reprap.GetPrintMonitor().EstimateTimeLeft(PrintEstimationMethod::fileBased))
+ : 0);
+ break;
+
+ case 37: // Print time remaining, filament-based
+ currentValue.SetDuration((reprap.GetPrintMonitor().IsPrinting())
+ ? static_cast<int>(reprap.GetPrintMonitor().EstimateTimeLeft(PrintEstimationMethod::filamentBased))
+ : 0);
+ break;
+
+ case 38: // requested speed
+ currentValue.SetFloat(reprap.GetMove().GetRequestedSpeedMmPerSec());
+ break;
+
+ case 39: // top speed
+ currentValue.SetFloat(reprap.GetMove().GetTopSpeedMmPerSec());
+ break;
+
+ default:
+ error = true;
+ }
+ break;
+
+ default:
+ error = true;
+ break;
+ }
+
+ itemChanged = (error || currentValue != oldValue);
+ }
+ }
+
+ if (itemChanged || !drawn || (highlight != highlighted))
+ {
+ highlighted = highlight;
+ PrintAligned(lcd, rightMargin);
+ itemChanged = false;
+ drawn = true;
+ }
+ }
+}
+
+bool ValueMenuItem::Select(const StringRef& cmd) noexcept
+{
+ adjusting = AdjustMode::adjusting;
+ return false;
+}
+
+void ValueMenuItem::UpdateWidthAndHeight(Lcd& lcd) noexcept
+{
+ // The width is always set for a ValueMenuItem so we just need to determine the height
+ if (height == 0)
+ {
+ lcd.SetFont(fontNumber);
+ height = lcd.GetFontHeight();
+ }
+}
+
+PixelNumber ValueMenuItem::GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept
+{
+ // TODO
+ return 0;
+}
+
+bool ValueMenuItem::Adjust_SelectHelper() noexcept
+{
+ if (adjusting == AdjustMode::adjusting)
+ {
+ const unsigned int itemNumber = GetReferencedToolNumber();
+
+ bool err = false;
+ switch (valIndex/100)
+ {
+ case 1: // heater active temperature
+ reprap.GetGCodes().SetItemActiveTemperature(itemNumber, (currentValue.fVal < 1.0) ? ABS_ZERO : currentValue.fVal);
+ break;
+
+ case 2: // heater standby temperature
+ reprap.GetGCodes().SetItemStandbyTemperature(itemNumber, (currentValue.fVal < 1.0) ? ABS_ZERO : currentValue.fVal);
+ break;
+
+ case 3: // fan %
+ if (itemNumber == 99)
+ {
+ reprap.GetGCodes().SetMappedFanSpeed(nullptr, currentValue.fVal * 0.01);
+ }
+ else
+ {
+ reprap.GetFansManager().SetFanValue(itemNumber, currentValue.fVal * 0.01);
+ }
+ break;
+
+ case 4: // extruder %
+ reprap.GetGCodes().SetExtrusionFactor(itemNumber, currentValue.fVal * 0.01);
+ break;
+
+ case 5: // misc.
+ switch (itemNumber)
+ {
+ case 0:
+ reprap.GetGCodes().SetPrimarySpeedFactor(currentValue.fVal * 0.01);
+ break;
+
+ case 20:
+ reprap.GetGCodes().SelectPrimaryTool(currentValue.iVal, false);
+ break;
+
+ case 21: // baby stepping
+ break;
+
+ default:
+ err = true;
+ break;
+ }
+ break;
+
+ default:
+ err = true;
+ break;
+ }
+
+ if (err)
+ {
+ reprap.GetDisplay().ErrorBeep();
+ }
+ }
+
+ adjusting = AdjustMode::displaying;
+ return true;
+}
+
+unsigned int ValueMenuItem::GetReferencedToolNumber() const noexcept
+{
+ unsigned int uToolNumber = valIndex % 100;
+ if (79 == uToolNumber)
+ {
+ uToolNumber = reprap.GetGCodes().GetPrimaryMovementState().GetCurrentToolNumber();
+ }
+
+ return uToolNumber;
+}
+
+// Adjust the value of this item by 'clicks' click of the encoder. 'clicks' is nonzero.
+// Return true if we have finished adjusting it.
+bool ValueMenuItem::Adjust_AlterHelper(int clicks) noexcept
+{
+ itemChanged = true; // we will probably change the value, so it will need to be re-displayed
+ const unsigned int itemNumber = GetReferencedToolNumber();
+
+ switch (valIndex/100)
+ {
+ case 1: // heater active temperature
+ case 2: // heater standby temperature
+ if (itemNumber < 80) // Tool heaters
+ {
+ // If we're decreasing, make any value smaller than 95 go to 0
+ // If we're increasing, make any value between 0 and 95 jump directly to 95
+ // Also cap the maximum
+ if (0 > clicks) // decrementing
+ {
+ currentValue.fVal += (float)clicks;
+ if (95.0 > currentValue.fVal)
+ {
+ currentValue.fVal = 0.0;
+ }
+ }
+ else // incrementing
+ {
+ if (0.0 == currentValue.fVal)
+ {
+ currentValue.fVal = 95.0 - 1.0;
+ }
+ currentValue.fVal = min<int>(currentValue.fVal + (float)clicks, reprap.GetHeat().GetHighestTemperatureLimit(Tool::GetLockedTool(itemNumber)->GetHeater(0)));
+ }
+ }
+ else
+ {
+ currentValue.fVal += (float)clicks;
+ }
+ break;
+
+ case 3: // fan %
+ currentValue.fVal = constrain<float>(currentValue.fVal + (float)clicks, 0.0, 100.0);
+ break;
+
+ case 5: // misc.
+ switch (itemNumber)
+ {
+ case 0: // 500 Feed Rate
+ currentValue.fVal = constrain<float>(currentValue.fVal + (float)clicks, 10.0, 500.0);
+ break;
+
+ case 20: // 520 Tool Selection
+ currentValue.iVal = constrain<int>((int)currentValue.iVal + clicks, -1, 255);
+ break;
+
+ case 21: // 521 baby stepping
+ {
+ String<ShortGCodeLength> cmd;
+ cmd.printf("M290 Z%.2f", (double)(0.02 * clicks));
+ (void) reprap.GetGCodes().ProcessCommandFromLcd(cmd.c_str());
+ adjusting = AdjustMode::liveAdjusting;
+ }
+ break;
+
+ default:
+ if (itemNumber >= 10 && itemNumber < 10 + reprap.GetGCodes().GetVisibleAxes()) // 510-518 axis position adjustment
+ {
+ String<ShortGCodeLength> cmd;
+ const float amount = ((itemNumber == 12) ? 0.02 : 0.1) * clicks; // 0.02mm Z resolution, 0.1mm for other axes
+ cmd.printf("M120 G91 G1 F3000 %c%.2f M121", 'X' + (itemNumber - 10), (double)amount);
+ (void) reprap.GetGCodes().ProcessCommandFromLcd(cmd.c_str());
+ adjusting = AdjustMode::liveAdjusting;
+ }
+ break;
+ }
+ break;
+
+ default:
+ currentValue.fVal += (float)clicks;
+ break;
+ }
+
+ return false;
+}
+
+// Adjust this element, returning true if we have finished adjustment.
+// 'clicks' is the number of encoder clicks to adjust by, or 0 if the button was pushed.
+bool ValueMenuItem::Adjust(int clicks) noexcept
+{
+ return (clicks == 0) // if button has been pressed
+ ? Adjust_SelectHelper()
+ : Adjust_AlterHelper(clicks);
+}
+
+#endif
+
+// End
diff --git a/src/Display/ValueMenuItem.h b/src/Display/ValueMenuItem.h
new file mode 100644
index 00000000..2660bb7e
--- /dev/null
+++ b/src/Display/ValueMenuItem.h
@@ -0,0 +1,59 @@
+/*
+ * ValueMenuItem.h
+ *
+ * Created on: 25 Apr 2022
+ * Author: David
+ */
+
+#ifndef SRC_DISPLAY_VALUEMENUITEM_H_
+#define SRC_DISPLAY_VALUEMENUITEM_H_
+
+#include "MenuItem.h"
+
+#if SUPPORT_DIRECT_LCD
+
+#include <ObjectModel/ObjectModel.h>
+
+class ValueMenuItem final : public MenuItem
+{
+public:
+ DECLARE_FREELIST_NEW_DELETE(ValueMenuItem)
+
+ ValueMenuItem(PixelNumber r, PixelNumber c, PixelNumber w, Alignment a, FontNumber fn, bool adj, const char *_ecv_array _ecv_null om, unsigned int v, unsigned int d) noexcept;
+ void Draw(Lcd& lcd, PixelNumber maxWidth, bool highlight) noexcept override;
+ bool Select(const StringRef& cmd) noexcept override;
+ bool CanAdjust() const noexcept override { return true; }
+ bool Adjust(int clicks) noexcept override;
+ void UpdateWidthAndHeight(Lcd& lcd) noexcept override;
+
+ PixelNumber GetVisibilityRowOffset(PixelNumber tCurrentOffset, PixelNumber fontHeight) const noexcept override;
+
+ unsigned int GetReferencedToolNumber() const noexcept;
+
+protected:
+ void CorePrint(Lcd& lcd) noexcept override;
+
+private:
+ enum class AdjustMode : uint8_t { displaying, adjusting, liveAdjusting };
+
+ bool Adjust_SelectHelper() noexcept;
+ bool Adjust_AlterHelper(int clicks) noexcept;
+
+ static constexpr PixelNumber DefaultWidth = 25; // default numeric field width
+
+ ExpressionValue currentValue; // the last value fetched for the item
+ const char *_ecv_array _ecv_null const omText; // an object model expression to fetch the item value (optional)
+ const unsigned int valIndex; // the item index, if no object model expression was provided
+ const char *_ecv_array _ecv_null textValue; // for temporary use when printing
+
+ const uint8_t decimals : 4,
+ adjustable : 1;
+ AdjustMode adjusting;
+ uint8_t error : 1, // for temporary use when printing
+ asPercent : 1; // true if we print this as a percentage
+};
+
+
+#endif
+
+#endif /* SRC_DISPLAY_VALUEMENUITEM_H_ */
diff --git a/src/Endstops/EndstopsManager.cpp b/src/Endstops/EndstopsManager.cpp
index 28c2564a..feb6b9de 100644
--- a/src/Endstops/EndstopsManager.cpp
+++ b/src/Endstops/EndstopsManager.cpp
@@ -38,58 +38,59 @@ ReadWriteLock EndstopsManager::zProbesLock;
// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM.
// Macro to build a standard lambda function that includes the necessary type conversions
-#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(EndstopsManager, __VA_ARGS__)
+#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(EndstopsManager, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor EndstopsManager::sensorsArrayDescriptor =
+constexpr ObjectModelArrayTableEntry EndstopsManager::objectModelArrayTable[] =
{
- &Heat::sensorsLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetHeat().GetNumSensorsToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(reprap.GetHeat().FindSensor(context.GetLastIndex()).Ptr()); }
-};
-
-constexpr ObjectModelArrayDescriptor EndstopsManager::endstopsArrayDescriptor =
-{
- &endstopsLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const EndstopsManager*)self)->FindEndstop(context.GetLastIndex()).Ptr()); }
-};
-
-constexpr ObjectModelArrayDescriptor EndstopsManager::filamentMonitorsArrayDescriptor =
-{
- &FilamentMonitor::filamentMonitorsLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return FilamentMonitor::GetNumMonitorsToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(FilamentMonitor::GetMonitorAlreadyLocked(context.GetLastIndex())); }
-};
-
-constexpr ObjectModelArrayDescriptor EndstopsManager::gpinArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetPlatform().GetNumGpInputsToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- {
- const GpInputPort& port = reprap.GetPlatform().GetGpInPort(context.GetLastIndex());
- return (port.IsUnused()) ? ExpressionValue(nullptr) : ExpressionValue(&port);
- }
+ // 0. Analog sensors
+ {
+ &Heat::sensorsLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetHeat().GetNumSensorsToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(reprap.GetHeat().FindSensor(context.GetLastIndex()).Ptr()); }
+ },
+ // 1. Axis endstops
+ {
+ &endstopsLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const EndstopsManager*)self)->FindEndstop(context.GetLastIndex()).Ptr()); }
+ },
+ // 2. Filament monitors
+ {
+ &FilamentMonitor::filamentMonitorsLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return FilamentMonitor::GetNumMonitorsToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(FilamentMonitor::GetMonitorAlreadyLocked(context.GetLastIndex())); }
+ },
+ // 3. GpIn ports
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetPlatform().GetNumGpInputsToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ {
+ const GpInputPort& port = reprap.GetPlatform().GetGpInPort(context.GetLastIndex());
+ return (port.IsUnused()) ? ExpressionValue(nullptr) : ExpressionValue(&port);
+ }
+ },
+ // 4. Z probes
+ {
+ &zProbesLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const EndstopsManager*)self)->GetNumProbesToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const EndstopsManager*)self)->GetZProbe(context.GetLastIndex()).Ptr()); }
+ }
};
-constexpr ObjectModelArrayDescriptor EndstopsManager::probesArrayDescriptor =
-{
- &zProbesLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const EndstopsManager*)self)->GetNumProbesToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const EndstopsManager*)self)->GetZProbe(context.GetLastIndex()).Ptr()); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(EndstopsManager)
constexpr ObjectModelTableEntry EndstopsManager::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. sensors members
- { "analog", OBJECT_MODEL_FUNC_NOSELF(&sensorsArrayDescriptor), ObjectModelEntryFlags::live },
- { "endstops", OBJECT_MODEL_FUNC_NOSELF(&endstopsArrayDescriptor), ObjectModelEntryFlags::live },
- { "filamentMonitors", OBJECT_MODEL_FUNC_NOSELF(&filamentMonitorsArrayDescriptor), ObjectModelEntryFlags::live },
- { "gpIn", OBJECT_MODEL_FUNC_NOSELF(&gpinArrayDescriptor), ObjectModelEntryFlags::live },
- { "probes", OBJECT_MODEL_FUNC_NOSELF(&probesArrayDescriptor), ObjectModelEntryFlags::live },
+ { "analog", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::live },
+ { "endstops", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::live },
+ { "filamentMonitors", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::live },
+ { "gpIn", OBJECT_MODEL_FUNC_ARRAY(3), ObjectModelEntryFlags::live },
+ { "probes", OBJECT_MODEL_FUNC_ARRAY(4), ObjectModelEntryFlags::live },
};
constexpr uint8_t EndstopsManager::objectModelTableDescriptor[] = { 1, 5 };
@@ -144,21 +145,18 @@ void EndstopsManager::Init() noexcept
ReadLockedPointer<Endstop> EndstopsManager::FindEndstop(size_t axis) const noexcept
{
- ReadLocker lock(endstopsLock);
- return ReadLockedPointer<Endstop>(lock, (axis < MaxAxes) ? axisEndstops[axis] : nullptr);
+ return ReadLockedPointer<Endstop>(endstopsLock, (axis < MaxAxes) ? axisEndstops[axis] : nullptr);
}
ReadLockedPointer<ZProbe> EndstopsManager::GetZProbe(size_t index) const noexcept
{
- ReadLocker lock(zProbesLock);
- return ReadLockedPointer<ZProbe>(lock, (index < ARRAY_SIZE(zProbes)) ? zProbes[index] : nullptr);
+ return ReadLockedPointer<ZProbe>(zProbesLock, (index < ARRAY_SIZE(zProbes)) ? zProbes[index] : nullptr);
}
// Return the current Z probe if there is one, else a default Z probe
ReadLockedPointer<ZProbe> EndstopsManager::GetZProbeOrDefault(size_t index) const noexcept
{
- ReadLocker lock(zProbesLock);
- return ReadLockedPointer<ZProbe>(lock,
+ return ReadLockedPointer<ZProbe>(zProbesLock,
(index < ARRAY_SIZE(zProbes) && zProbes[index] != nullptr)
? zProbes[index]
: defaultZProbe);
@@ -178,7 +176,13 @@ void EndstopsManager::AddToActive(EndstopOrZProbe& e) noexcept
activeEndstops = &e;
}
-// Set up the active endstop list according to the axes commanded to move in a G0/G1 S1/S3 command. Return true if successful.
+// Set no active endstops
+void EndstopsManager::ClearEndstops() noexcept
+{
+ activeEndstops = nullptr;
+}
+
+// Clear any existing endstops and set up the active endstop list according to the axes commanded to move in a G0/G1 S1/S3 command. Return true if successful.
bool EndstopsManager::EnableAxisEndstops(AxesBitmap axes, bool forHoming, bool& reduceAcceleration) noexcept
{
activeEndstops = nullptr;
diff --git a/src/Endstops/EndstopsManager.h b/src/Endstops/EndstopsManager.h
index 7221b014..ba72ce20 100644
--- a/src/Endstops/EndstopsManager.h
+++ b/src/Endstops/EndstopsManager.h
@@ -29,13 +29,16 @@ public:
void Init() noexcept;
+ // Set no active endstops
+ void ClearEndstops() noexcept;
+
// Set up the active endstop list according to the axes commanded to move in a G0/G1 S1/S3 command returning true if successful
bool EnableAxisEndstops(AxesBitmap axes, bool forHoming, bool& reduceAcceleration) noexcept __attribute__ ((warn_unused_result));
- // Set up the active endstops for Z probing returning true if successful
+ // Clear all endstops then set up the active endstops for Z probing returning true if successful
bool EnableZProbe(size_t probeNumber, bool probingAway = false) noexcept __attribute__ ((warn_unused_result));
- // Enable extruder endstops
+ // Enable extruder endstops, adding to any axis endstops already set up
bool EnableExtruderEndstops(ExtrudersBitmap extruders) noexcept;
// Get the first endstop that has triggered and remove it from the active list if appropriate
@@ -73,12 +76,7 @@ public:
#endif
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(sensors)
- OBJECT_MODEL_ARRAY(endstops)
- OBJECT_MODEL_ARRAY(filamentMonitors)
- OBJECT_MODEL_ARRAY(gpin)
- OBJECT_MODEL_ARRAY(probes)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
// Add an endstop to the active list
diff --git a/src/Endstops/ZProbe.cpp b/src/Endstops/ZProbe.cpp
index 992b3708..9fb1d5a8 100644
--- a/src/Endstops/ZProbe.cpp
+++ b/src/Endstops/ZProbe.cpp
@@ -22,42 +22,44 @@
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(ZProbe, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor ZProbe::offsetsArrayDescriptor =
+constexpr ObjectModelArrayTableEntry ZProbe::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const ZProbe*)self)->offsets[context.GetLastIndex()], 2); }
-};
-
-constexpr ObjectModelArrayDescriptor ZProbe::valueArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return (((const ZProbe*)self)->type == ZProbeType::dumbModulated) ? 2 : 1; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { int v1 = 0;
- return ExpressionValue
- ( (context.GetLastIndex() == 0)
- ? (int32_t)((const ZProbe*)self)->GetReading()
- : (((const ZProbe*)self)->GetSecondaryValues(v1), v1)
- );
- }
-};
-
-constexpr ObjectModelArrayDescriptor ZProbe::temperatureCoefficientsArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ARRAY_SIZE(ZProbe::temperatureCoefficients); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const ZProbe*)self)->temperatureCoefficients[context.GetLastIndex()], 5); }
+ // 0. Offsets
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const ZProbe*)self)->offsets[context.GetLastIndex()], 2); }
+ },
+ // 1. Speeds
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ARRAY_SIZE(ZProbe::probeSpeeds); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(InverseConvertSpeedToMmPerMin(((const ZProbe*)self)->probeSpeeds[context.GetLastIndex()]), 1); }
+ },
+ // 2. Temperature coefficients
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ARRAY_SIZE(ZProbe::temperatureCoefficients); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const ZProbe*)self)->temperatureCoefficients[context.GetLastIndex()], 5); }
+ },
+ // 3. Values
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return (((const ZProbe*)self)->type == ZProbeType::dumbModulated) ? 2 : 1; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { int v1 = 0;
+ return ExpressionValue
+ ( (context.GetLastIndex() == 0)
+ ? (int32_t)((const ZProbe*)self)->GetReading()
+ : (((const ZProbe*)self)->GetSecondaryValues(v1), v1)
+ );
+ }
+ }
};
-constexpr ObjectModelArrayDescriptor ZProbe::speedsArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ARRAY_SIZE(ZProbe::probeSpeeds); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(InverseConvertSpeedToMmPerMin(((const ZProbe*)self)->probeSpeeds[context.GetLastIndex()]), 1); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(ZProbe)
constexpr ObjectModelTableEntry ZProbe::objectModelTable[] =
{
@@ -69,18 +71,18 @@ constexpr ObjectModelTableEntry ZProbe::objectModelTable[] =
{ "diveHeight", OBJECT_MODEL_FUNC(self->diveHeight, 1), ObjectModelEntryFlags::none },
{ "lastStopHeight", OBJECT_MODEL_FUNC(self->lastStopHeight, 3), ObjectModelEntryFlags::none },
{ "maxProbeCount", OBJECT_MODEL_FUNC((int32_t)self->misc.parts.maxTaps), ObjectModelEntryFlags::none },
- { "offsets", OBJECT_MODEL_FUNC_NOSELF(&offsetsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "offsets", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
{ "recoveryTime", OBJECT_MODEL_FUNC(self->recoveryTime, 1), ObjectModelEntryFlags::none },
{ "speed", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerMin(self->probeSpeeds[1]), 1), ObjectModelEntryFlags::obsolete },
- { "speeds", OBJECT_MODEL_FUNC_NOSELF(&speedsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "speeds", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
{ "temperatureCoefficient", OBJECT_MODEL_FUNC(self->temperatureCoefficients[0], 5), ObjectModelEntryFlags::obsolete },
- { "temperatureCoefficients", OBJECT_MODEL_FUNC_NOSELF(&temperatureCoefficientsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "temperatureCoefficients", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::none },
{ "threshold", OBJECT_MODEL_FUNC((int32_t)self->adcValue), ObjectModelEntryFlags::none },
{ "tolerance", OBJECT_MODEL_FUNC(self->tolerance, 3), ObjectModelEntryFlags::none },
{ "travelSpeed", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerMin(self->travelSpeed), 1), ObjectModelEntryFlags::none },
{ "triggerHeight", OBJECT_MODEL_FUNC(-self->offsets[Z_AXIS], 3), ObjectModelEntryFlags::none },
{ "type", OBJECT_MODEL_FUNC((int32_t)self->type), ObjectModelEntryFlags::none },
- { "value", OBJECT_MODEL_FUNC_NOSELF(&valueArrayDescriptor), ObjectModelEntryFlags::live },
+ { "value", OBJECT_MODEL_FUNC_ARRAY(3), ObjectModelEntryFlags::live },
};
constexpr uint8_t ZProbe::objectModelTableDescriptor[] = { 1, 18 };
diff --git a/src/Endstops/ZProbe.h b/src/Endstops/ZProbe.h
index 552fbb59..49811410 100644
--- a/src/Endstops/ZProbe.h
+++ b/src/Endstops/ZProbe.h
@@ -67,11 +67,7 @@ public:
static constexpr unsigned int MaxTapsLimit = 31; // must be low enough to fit in the maxTaps field
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(offsets)
- OBJECT_MODEL_ARRAY(value)
- OBJECT_MODEL_ARRAY(temperatureCoefficients)
- OBJECT_MODEL_ARRAY(speeds)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
uint8_t number;
ZProbeType type;
diff --git a/src/Fans/Fan.cpp b/src/Fans/Fan.cpp
index 17d53cef..8f9ccce4 100644
--- a/src/Fans/Fan.cpp
+++ b/src/Fans/Fan.cpp
@@ -34,12 +34,13 @@ constexpr ObjectModelTableEntry Fan::objectModelTable[] =
{ "thermostatic", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none },
// 1. Fan.thermostatic members
- { "heaters", OBJECT_MODEL_FUNC(self->sensorsMonitored), ObjectModelEntryFlags::none }, // empty if not thermostatic
+ { "heaters", OBJECT_MODEL_FUNC(self->sensorsMonitored), ObjectModelEntryFlags::obsolete }, // empty if not thermostatic
{ "highTemperature", OBJECT_MODEL_FUNC_IF(self->sensorsMonitored.IsNonEmpty(), self->triggerTemperatures[1], 1), ObjectModelEntryFlags::none },
{ "lowTemperature", OBJECT_MODEL_FUNC_IF(self->sensorsMonitored.IsNonEmpty(), self->triggerTemperatures[0], 1), ObjectModelEntryFlags::none },
+ { "sensors", OBJECT_MODEL_FUNC(self->sensorsMonitored), ObjectModelEntryFlags::none }, // empty if not thermostatic
};
-constexpr uint8_t Fan::objectModelTableDescriptor[] = { 2, 9, 3 };
+constexpr uint8_t Fan::objectModelTableDescriptor[] = { 2, 9, 4 };
DEFINE_GET_OBJECT_MODEL_TABLE(Fan)
diff --git a/src/Fans/FansManager.cpp b/src/Fans/FansManager.cpp
index e944938c..023dd8a1 100644
--- a/src/Fans/FansManager.cpp
+++ b/src/Fans/FansManager.cpp
@@ -37,8 +37,7 @@ FansManager::FansManager() noexcept
// Lock the fan system before calling this, so that the fan can't be deleted while we are accessing it.
ReadLockedPointer<Fan> FansManager::FindFan(size_t fanNum) const noexcept
{
- ReadLocker locker(fansLock);
- return ReadLockedPointer<Fan>(locker, (fanNum < ARRAY_SIZE(fans)) ? fans[fanNum] : nullptr);
+ return ReadLockedPointer<Fan>(fansLock, (fanNum < ARRAY_SIZE(fans)) ? fans[fanNum] : nullptr);
}
// Create and return a local fan. if it fails, return nullptr with the error message in 'reply'.
diff --git a/src/GCodes/CollisionAvoider.cpp b/src/GCodes/CollisionAvoider.cpp
new file mode 100644
index 00000000..8c5c879e
--- /dev/null
+++ b/src/GCodes/CollisionAvoider.cpp
@@ -0,0 +1,64 @@
+/*
+ * CollisionAvoider.cpp
+ *
+ * Created on: 15 Aug 2022
+ * Author: David
+ */
+
+#include <GCodes/CollisionAvoider.h>
+
+#if SUPPORT_ASYNC_MOVES
+
+#include <Platform/RepRap.h>
+#include <GCodes/GCodes.h>
+
+CollisionAvoider::CollisionAvoider() noexcept
+{
+ lowerAxis = upperAxis = -1;
+}
+
+bool CollisionAvoider::IsValid() const noexcept
+{
+ return lowerAxis >= 0 && upperAxis >= 0;
+}
+
+// Reset the position accumulators
+void CollisionAvoider::ResetPositions(const float positions[]) noexcept
+{
+ if (IsValid())
+ {
+ lowerAxisMax = positions[lowerAxis];
+ upperAxisMin = positions[upperAxis];
+ }
+}
+
+// Set the parameters
+void CollisionAvoider::Set(int axisL, int axisH, float sep, const float positions[]) noexcept
+{
+ lowerAxis = axisL;
+ upperAxis = axisH;
+ minSeparation = sep;
+ ResetPositions(positions);
+}
+
+bool CollisionAvoider::UpdatePositions(const float axisPositions[]) noexcept
+{
+ const float newLowerMax = max<float>(axisPositions[lowerAxis], lowerAxisMax);
+ const float newUpperMin = min<float>(axisPositions[upperAxis], upperAxisMin);
+ if (newLowerMax + minSeparation > newUpperMin)
+ {
+ if (reprap.Debug(moduleMove))
+ {
+ const char *const axisLetters = reprap.GetGCodes().GetAxisLetters();
+ debugPrintf("Potential collision between axis %c at %.1f and axis %c at %.1f\n", axisLetters[lowerAxis], (double)newLowerMax, axisLetters[upperAxis], (double)newUpperMin);
+ }
+ return false;
+ }
+ lowerAxisMax = newLowerMax;
+ upperAxisMin = newUpperMin;
+ return true;
+}
+
+#endif
+
+// End
diff --git a/src/GCodes/CollisionAvoider.h b/src/GCodes/CollisionAvoider.h
new file mode 100644
index 00000000..a8fda29e
--- /dev/null
+++ b/src/GCodes/CollisionAvoider.h
@@ -0,0 +1,46 @@
+/*
+ * CollisionAvoider.h
+ *
+ * Created on: 15 Aug 2022
+ * Author: David
+ */
+
+#ifndef SRC_GCODES_COLLISIONAVOIDER_H_
+#define SRC_GCODES_COLLISIONAVOIDER_H_
+
+#include <RepRapFirmware.h>
+
+#if SUPPORT_ASYNC_MOVES
+
+#include <Movement/RawMove.h>
+
+class CollisionAvoider
+{
+public:
+ CollisionAvoider() noexcept;
+
+ bool IsValid() const noexcept;
+ int GetLowerAxis() const noexcept { return lowerAxis; }
+ int GetUpperAxis() const noexcept { return upperAxis; }
+ float GetMinSeparation()const noexcept { return minSeparation; }
+
+ // Reset the position accumulators
+ void ResetPositions(const float positions[]) noexcept;
+
+ // If the new move doesn't risk a collision, update the position accumulators and return true; else return false
+ bool UpdatePositions(const float axisPositions[]) noexcept;
+
+ // Set the parameters
+ void Set(int axisL, int axisH, float sep, const float positions[]) noexcept;
+
+private:
+ float minSeparation;
+ float lowerAxisMax;
+ float upperAxisMin;
+ int lowerAxis;
+ int upperAxis;
+};
+
+#endif
+
+#endif /* SRC_GCODES_COLLISIONAVOIDER_H_ */
diff --git a/src/GCodes/GCodeBuffer/BinaryParser.cpp b/src/GCodes/GCodeBuffer/BinaryParser.cpp
index 3dcb2650..9c6f9d8f 100644
--- a/src/GCodes/GCodeBuffer/BinaryParser.cpp
+++ b/src/GCodes/GCodeBuffer/BinaryParser.cpp
@@ -368,7 +368,7 @@ void BinaryParser::GetIPAddress(IPAddress& returnedIp) THROWS(GCodeException)
case DataType::Expression:
//TODO not handled yet
default:
- throw ConstructParseException("IP address string expected");
+ throw ConstructParseException("expected a IP address");
}
seenParameter = nullptr;
@@ -420,7 +420,7 @@ void BinaryParser::GetMacAddress(MacAddress& mac) THROWS(GCodeException)
case DataType::Expression:
//TODO not handled yet
default:
- throw ConstructParseException("MAC address string expected");
+ throw ConstructParseException("expected a MAC address");
}
seenParameter = nullptr;
@@ -435,7 +435,7 @@ void BinaryParser::GetUnprecedentedString(const StringRef& str, bool allowEmpty)
}
else if (!allowEmpty)
{
- throw ConstructParseException("non-empty string expected");
+ throw ConstructParseException("expected a non-empty string");
}
}
@@ -470,7 +470,7 @@ void BinaryParser::GetPossiblyQuotedString(const StringRef& str, bool allowEmpty
seenParameterValue = nullptr;
if (!allowEmpty && str.IsEmpty())
{
- throw ConstructParseException("non-empty string expected");
+ throw ConstructParseException("expected a non-empty string");
}
}
@@ -570,6 +570,24 @@ void BinaryParser::GetDriverIdArray(DriverId arr[], size_t& length) THROWS(GCode
}
}
+// Get a :-separated list of strings after a key letter
+ExpressionValue BinaryParser::GetExpression() THROWS(GCodeException)
+{
+ if (seenParameter == nullptr)
+ {
+ THROW_INTERNAL_ERROR;
+ }
+
+ if (seenParameter->type == DataType::Expression)
+ {
+ ExpressionParser parser(gb, seenParameterValue, seenParameterValue + seenParameter->intValue, -1);
+ const ExpressionValue val = parser.Parse();
+ parser.CheckForExtraCharacters();
+ return val;
+ }
+ throw ConstructParseException("expected an expression inside { }");
+}
+
void BinaryParser::SetFinished() noexcept
{
gb.LatestMachineState().g53Active = false; // G53 does not persist beyond the current command
diff --git a/src/GCodes/GCodeBuffer/BinaryParser.h b/src/GCodes/GCodeBuffer/BinaryParser.h
index 3c1dcbda..199d1901 100644
--- a/src/GCodes/GCodeBuffer/BinaryParser.h
+++ b/src/GCodes/GCodeBuffer/BinaryParser.h
@@ -50,6 +50,7 @@ public:
void GetIntArray(int32_t arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of ints after a key letter
void GetUnsignedArray(uint32_t arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of unsigned ints after a key letter
void GetDriverIdArray(DriverId arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of drivers after a key letter
+ ExpressionValue GetExpression() THROWS(GCodeException); // Get an expression after a key letter
void SetFinished() noexcept; // Set the G Code finished
diff --git a/src/GCodes/GCodeBuffer/ExpressionParser.cpp b/src/GCodes/GCodeBuffer/ExpressionParser.cpp
index f6b882b6..fdc112dd 100644
--- a/src/GCodes/GCodeBuffer/ExpressionParser.cpp
+++ b/src/GCodes/GCodeBuffer/ExpressionParser.cpp
@@ -32,7 +32,7 @@ namespace StackUsage
}
// These can't be declared locally inside ParseIdentifierExpression because NamedEnum includes static data
-NamedEnum(NamedConstant, unsigned int, _false, iterations, line, _null, pi, _result, _true);
+NamedEnum(NamedConstant, unsigned int, _false, iterations, line, _null, pi, _result, _true, input);
NamedEnum(Function, unsigned int, abs, acos, asin, atan, atan2, cos, datetime, degrees, exists, floor, isnan, max, min, mod, radians, random, sin, sqrt, tan);
const char * const InvalidExistsMessage = "invalid 'exists' expression";
@@ -47,11 +47,90 @@ void ExpressionParser::ParseExpectKet(ExpressionValue& rslt, bool evaluate, char
{
CheckStack(StackUsage::ParseInternal);
ParseInternal(rslt, evaluate, 0);
- if (CurrentCharacter() != closingBracket)
+ if (CurrentCharacter() == closingBracket)
+ {
+ AdvancePointer();
+ }
+ else if (CurrentCharacter() == ',' && closingBracket == '}')
+ {
+ ParseGeneralArray(rslt, evaluate);
+ }
+ else
{
ThrowParseException("expected '%c'", (uint32_t)closingBracket);
}
- AdvancePointer();
+
+ // Check for trailing index expressions
+ for (;;)
+ {
+ SkipWhiteSpace();
+ if (CurrentCharacter() != '[')
+ {
+ break;
+ }
+ const int indexCol = GetColumn();
+ AdvancePointer();
+ const uint32_t indexValue = ParseUnsigned();
+ if (CurrentCharacter() != ']')
+ {
+ ThrowParseException("expected ']'");
+ }
+ AdvancePointer();
+ switch (rslt.GetType())
+ {
+ case TypeCode::ObjectModelArray:
+ {
+ const ObjectModelArrayTableEntry *const entry = rslt.omVal->FindObjectModelArrayEntry(rslt.param & 0xFF);
+ if (entry == nullptr)
+ {
+ THROW_INTERNAL_ERROR;
+ }
+ ObjectExplorationContext context;
+ context.AddIndex(rslt.param >> 16);
+ ReadLocker lock(entry->lockPointer);
+ const size_t numElements = entry->GetNumElements(rslt.omVal, context);
+ if (indexValue < numElements)
+ {
+ context.AddIndex(indexValue);
+ rslt = entry->GetElement(rslt.omVal, context);
+ }
+ else if (evaluate)
+ {
+ throw GCodeException(gb.GetLineNumber(), indexCol, "array index out of range");
+ }
+ else
+ {
+ rslt.SetNull(nullptr);
+ }
+ }
+ break;
+
+ case TypeCode::HeapArray:
+ {
+ if (indexValue < rslt.ahVal.GetNumElements())
+ {
+ rslt.ahVal.GetElement(indexValue, rslt);
+ }
+ else if (evaluate)
+ {
+ throw GCodeException(gb.GetLineNumber(), indexCol, "array index out of range");
+ }
+ else
+ {
+ rslt.SetNull(nullptr);
+ }
+ }
+ break;
+
+ default:
+ if (evaluate)
+ {
+ throw GCodeException(gb.GetLineNumber(), indexCol, "left operand of [ ] is not an array");
+ }
+ rslt.SetNull(nullptr);
+ break;
+ }
+ }
}
// Evaluate an expression. Do not call this one recursively!
@@ -142,18 +221,7 @@ void ExpressionParser::ParseInternal(ExpressionValue& val, bool evaluate, uint8_
{
CheckStack(StackUsage::ParseInternal);
ParseInternal(val, evaluate, UnaryPriority);
- if (val.GetType() == TypeCode::CString)
- {
- val.SetInt((int32_t)strlen(val.sVal));
- }
- else if (val.GetType() == TypeCode::HeapString)
- {
- val.SetInt((int32_t)val.shVal.GetLength());
- }
- else
- {
- ThrowParseException("expected object model value or string after '#");
- }
+ ApplyLengthOperator(val);
}
break;
@@ -641,6 +709,41 @@ void ExpressionParser::ParseDriverIdArray(DriverId arr[], size_t& length) THROWS
ParseArray(length, [this, &arr](size_t index) { arr[index] = ParseDriverId(); });
}
+// Parse the rest of an array. We have already parsed the first element and found but not skipped a comma. The array should be terminated with '}'.
+void ExpressionParser::ParseGeneralArray(ExpressionValue& firstElementAndResult, bool evaluate) THROWS(GCodeException)
+{
+ // Parse the array elements into a temporary array
+ ExpressionValue elements[10];
+ elements[0] = std::move(firstElementAndResult);
+ size_t index = 1;
+ do
+ {
+ AdvancePointer(); // skip the comma
+ SkipWhiteSpace(); // skip any following white space
+ if (CurrentCharacter() == '}')
+ {
+ break; // we allow a trailing comma and it can be used to distinguish a 1-element array from a bracketed value
+ }
+ ParseInternal(elements[index], evaluate, 0);
+ ++index;
+ } while (index < ARRAY_SIZE(elements) && CurrentCharacter() == ',');
+
+ if (CurrentCharacter() != '}')
+ {
+ ThrowParseException("expected '}'");
+ }
+ AdvancePointer();
+
+ // Copy the temporary array to the heap
+ ArrayHandle ah;
+ ah.Allocate(index);
+ for (size_t i = 0; i < index; ++i)
+ {
+ ah.AssignElement(i, elements[i]);
+ }
+ firstElementAndResult.SetArrayHandle(ah);
+}
+
void ExpressionParser::BalanceNumericTypes(ExpressionValue& val1, ExpressionValue& val2, bool evaluate) const THROWS(GCodeException)
{
// First convert any Uint64 or Uint32 operands to float
@@ -839,6 +942,45 @@ void ExpressionParser::ConvertToDriverId(ExpressionValue& val, bool evaluate) co
}
}
+void ExpressionParser::ApplyLengthOperator(ExpressionValue& val) const THROWS(GCodeException)
+{
+ switch (val.GetType())
+ {
+ case TypeCode::CString:
+ val.SetInt((int32_t)strlen(val.sVal));
+ break;
+
+ case TypeCode::HeapString:
+ val.SetInt((int32_t)val.shVal.GetLength());
+ break;
+
+ case TypeCode::ObjectModelArray:
+ {
+ const ObjectModelArrayTableEntry *const entry = val.omVal->FindObjectModelArrayEntry(val.param & 0xFF);
+ if (entry == nullptr)
+ {
+ THROW_INTERNAL_ERROR;
+ }
+ ObjectExplorationContext context;
+ context.AddIndex(val.param >> 16);
+ ReadLocker lock(entry->lockPointer);
+ val.SetInt(entry->GetNumElements(val.omVal, context));
+ context.RemoveIndex();
+ }
+ break;
+
+ case TypeCode::HeapArray:
+ {
+ ReadLocker lock(Heap::heapLock);
+ val.SetInt(val.ahVal.GetNumElements());
+ }
+ break;
+
+ default:
+ ThrowParseException("expected object model value or string after '#");
+ }
+}
+
void ExpressionParser::SkipWhiteSpace() noexcept
{
char c;
@@ -879,7 +1021,8 @@ void ExpressionParser::ParseNumber(ExpressionValue& rslt) noexcept
// *** This function is recursive, so keep its stack usage low!
void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool evaluate, bool applyLengthOperator, bool applyExists) THROWS(GCodeException)
{
- if (!isalpha(CurrentCharacter()))
+ char c = CurrentCharacter();
+ if (!isalpha(c))
{
ThrowParseException("expected an identifier");
}
@@ -889,8 +1032,8 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
// Loop parsing identifiers and index expressions
// When we come across an index expression, evaluate it, add it to the context, and place a marker in the identifier string.
- char c;
- while (isalpha((c = CurrentCharacter())) || isdigit(c) || c == '_' || c == '.' || c == '[')
+ bool isIdentifierCharacter = true;
+ do
{
AdvancePointer();
if (c == '[')
@@ -914,11 +1057,30 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
context.ProvideIndex(index.iVal);
c = '^'; // add the marker
}
+
if (id.cat(c))
{
- ThrowParseException("variable name too long");;
+ ThrowParseException("variable name too long");
}
- }
+
+ // Get the next character, skipping white space that is not inside an identifier
+ bool hadIdentifierSpace = false;
+ for (;;)
+ {
+ c = CurrentCharacter();
+ if (c != ' ' && c != '\t')
+ {
+ break;
+ }
+ hadIdentifierSpace = isIdentifierCharacter;
+ AdvancePointer();
+ }
+ isIdentifierCharacter = (isalpha(c) || isdigit(c) || c == '_');
+ if (isIdentifierCharacter && hadIdentifierSpace)
+ {
+ break; // don't allow spaces inside identifiers
+ }
+ } while (isIdentifierCharacter || c == '.' || c == '[');
// Check for the names of constants
NamedConstant whichConstant(id.c_str());
@@ -972,6 +1134,10 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
res = 1;
break;
+ case GCodeResult::m291Cancelled:
+ res = -1;
+ break;
+
default:
res = 2;
break;
@@ -984,6 +1150,10 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
rslt.SetInt((int32_t)gb.GetLineNumber());
return;
+ case NamedConstant::input:
+ rslt = gb.GetM291Result();
+ return;
+
default:
THROW_INTERNAL_ERROR;
}
@@ -1160,31 +1330,6 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
break;
case Function::max:
- for (;;)
- {
- SkipWhiteSpace();
- if (CurrentCharacter() != ',')
- {
- break;
- }
- AdvancePointer();
- SkipWhiteSpace();
- ExpressionValue nextOperand;
- // We recently checked the stack for a call to ParseInternal, no need to do it again
- ParseInternal(nextOperand, evaluate, 0);
- BalanceNumericTypes(rslt, nextOperand, evaluate);
- if (rslt.GetType() == TypeCode::Float)
- {
- rslt.fVal = max<float>(rslt.fVal, nextOperand.fVal);
- rslt.param = max(rslt.param, nextOperand.param);
- }
- else
- {
- rslt.iVal = max<int32_t>(rslt.iVal, nextOperand.iVal);
- }
- }
- break;
-
case Function::min:
for (;;)
{
@@ -1201,12 +1346,12 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
BalanceNumericTypes(rslt, nextOperand, evaluate);
if (rslt.GetType() == TypeCode::Float)
{
- rslt.fVal = min<float>(rslt.fVal, nextOperand.fVal);
+ rslt.fVal = ((func.RawValue() == Function::min) ? min<float> : max<float>)(rslt.fVal, nextOperand.fVal);
rslt.param = max(rslt.param, nextOperand.param);
}
else
{
- rslt.iVal = min<int32_t>(rslt.iVal, nextOperand.iVal);
+ rslt.iVal = ((func.RawValue() == Function::min) ? min<int32_t> : max<int32_t>)(rslt.iVal, nextOperand.iVal);
}
}
break;
@@ -1283,20 +1428,20 @@ void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool eva
// Check for a parameter, local or global variable
if (StringStartsWith(id.c_str(), "param."))
{
- GetVariableValue(rslt, &gb.GetVariables(), id.c_str() + strlen("param."), true, applyExists);
+ GetVariableValue(rslt, &gb.GetVariables(), id.c_str() + strlen("param."), context, true, applyLengthOperator, applyExists);
return;
}
if (StringStartsWith(id.c_str(), "global."))
{
auto vars = reprap.GetGlobalVariablesForReading();
- GetVariableValue(rslt, vars.Ptr(), id.c_str() + strlen("global."), false, applyExists);
+ GetVariableValue(rslt, vars.Ptr(), id.c_str() + strlen("global."), context, false, applyLengthOperator, applyExists);
return;
}
if (StringStartsWith(id.c_str(), "var."))
{
- GetVariableValue(rslt, &gb.GetVariables(), id.c_str() + strlen("var."), false, applyExists);
+ GetVariableValue(rslt, &gb.GetVariables(), id.c_str() + strlen("var."), context, false, applyLengthOperator, applyExists);
return;
}
@@ -1331,23 +1476,81 @@ time_t ExpressionParser::ParseDateTime(const char *s) const THROWS(GCodeExceptio
return mktime(&timeInfo);
}
-// Get the value of a variable
-void ExpressionParser::GetVariableValue(ExpressionValue& rslt, const VariableSet *vars, const char *name, bool parameter, bool wantExists) THROWS(GCodeException)
+// Get the value of a variable or part of a variable
+void ExpressionParser::GetVariableValue(ExpressionValue& rslt, const VariableSet *vars, const char *name, ObjectExplorationContext& context, bool isParameter, bool applyLengthOperator, bool wantExists) THROWS(GCodeException)
{
- const Variable* var = vars->Lookup(name);
- if (wantExists)
+ const char *pos = strchr(name, '^');
+ if (pos != nullptr)
{
- rslt.SetBool(var != nullptr);
- return;
- }
+ // Indexing into a variable
+ const Variable *const var = vars->Lookup(name, pos - name);
+ if (var != nullptr)
+ {
+ ExpressionValue val = var->GetValue();
+ while (val.GetType() == TypeCode::HeapArray)
+ {
+ ExpressionValue elem;
+ {
+ context.AddIndex(); // we are about to use up an index
+ const int32_t index = context.GetLastIndex();
+ ReadLocker lock(Heap::heapLock);
+ if ((unsigned int)index > val.ahVal.GetNumElements())
+ {
+ ThrowParseException("Index out of range");
+ }
+ val.ahVal.GetElement(index, elem);
+ }
- if (var != nullptr && (!parameter || var->GetScope() < 0))
+ ++pos; // skip the '^'
+ if (*pos == 0)
+ {
+ // End of the expression
+ rslt = elem;
+ return;
+ }
+ if (*pos == '^')
+ {
+ val = elem;
+ continue;
+ }
+ if (*pos == '.' && elem.GetType() == TypeCode::ObjectModel_tc)
+ {
+ rslt = elem.omVal->GetObjectValueUsingTableNumber(context, nullptr, pos + 1, 0);
+ return;
+ }
+ ThrowParseException("Error indexing into nested arrays");
+ }
+ ThrowParseException("Cannot index into variable or parameter '%s' of non-array type", name);
+ }
+ else if (wantExists)
+ {
+ rslt.SetBool(false);
+ return;
+ }
+ // else fall through to throw error
+ }
+ else
{
- rslt = var->GetValue();
- return;
+ const Variable *const var = vars->Lookup(name, strlen(name));
+ if (wantExists)
+ {
+ rslt.SetBool(var != nullptr);
+ return;
+ }
+
+ if (var != nullptr && (!isParameter || var->GetScope() < 0))
+ {
+ rslt = var->GetValue();
+ if (applyLengthOperator)
+ {
+ ApplyLengthOperator(rslt);
+ }
+ return;
+ }
+ // else fall through to throw error
}
- ThrowParseException((parameter) ? "unknown parameter '%s'" : "unknown variable '%s'", name);
+ ThrowParseException((isParameter) ? "unknown parameter '%s'" : "unknown variable '%s'", name);
}
// Parse a quoted string, given that the current character is double-quote
diff --git a/src/GCodes/GCodeBuffer/ExpressionParser.h b/src/GCodes/GCodeBuffer/ExpressionParser.h
index 2fbee391..632549a6 100644
--- a/src/GCodes/GCodeBuffer/ExpressionParser.h
+++ b/src/GCodes/GCodeBuffer/ExpressionParser.h
@@ -49,15 +49,18 @@ private:
pre(readPointer >= 0; isalpha(gb.buffer[readPointer]));
void __attribute__((noinline)) ParseQuotedString(ExpressionValue& rslt) THROWS(GCodeException);
+ void ParseGeneralArray(ExpressionValue& firstElementAndResult, bool evaluate) THROWS(GCodeException);
+
void ParseArray(size_t& length, function_ref<void(size_t index) THROWS(GCodeException)> processElement) THROWS(GCodeException);
time_t ParseDateTime(const char *s) const THROWS(GCodeException);
- void GetVariableValue(ExpressionValue& rslt, const VariableSet *vars, const char *name, bool parameter, bool wantExists) THROWS(GCodeException);
+ void GetVariableValue(ExpressionValue& rslt, const VariableSet *vars, const char *name, ObjectExplorationContext& context, bool isParameter, bool applyLengthOperator, bool wantExists) THROWS(GCodeException);
void ConvertToFloat(ExpressionValue& val, bool evaluate) const THROWS(GCodeException);
void ConvertToBool(ExpressionValue& val, bool evaluate) const THROWS(GCodeException);
void ConvertToString(ExpressionValue& val, bool evaluate) noexcept;
void ConvertToDriverId(ExpressionValue& val, bool evaluate) const THROWS(GCodeException);
+ void ApplyLengthOperator(ExpressionValue& val) const THROWS(GCodeException);
void CheckStack(uint32_t calledFunctionStackUsage) const THROWS(GCodeException);
diff --git a/src/GCodes/GCodeBuffer/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer/GCodeBuffer.cpp
index dd63a541..91a7369c 100644
--- a/src/GCodes/GCodeBuffer/GCodeBuffer.cpp
+++ b/src/GCodes/GCodeBuffer/GCodeBuffer.cpp
@@ -56,15 +56,20 @@ constexpr ObjectModelTableEntry GCodeBuffer::objectModelTable[] =
{ "drivesRelative", OBJECT_MODEL_FUNC((bool)self->machineState->drivesRelative), ObjectModelEntryFlags::none },
{ "feedRate", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerSec(self->machineState->feedRate), 1), ObjectModelEntryFlags::live },
{ "inMacro", OBJECT_MODEL_FUNC((bool)self->machineState->doingFileMacro), ObjectModelEntryFlags::live },
+ { "inverseTimeMode", OBJECT_MODEL_FUNC((bool)self->machineState->inverseTimeMode), ObjectModelEntryFlags::none },
{ "lineNumber", OBJECT_MODEL_FUNC((int32_t)self->GetLineNumber()), ObjectModelEntryFlags::live },
{ "macroRestartable", OBJECT_MODEL_FUNC((bool)self->machineState->macroRestartable), ObjectModelEntryFlags::none },
+#if SUPPORT_ASYNC_MOVES
+ { "motionSystem", OBJECT_MODEL_FUNC((int32_t)self->GetActiveQueueNumber()), ObjectModelEntryFlags::live },
+#endif
{ "name", OBJECT_MODEL_FUNC(self->codeChannel.ToString()), ObjectModelEntryFlags::none },
+ { "selectedPlane", OBJECT_MODEL_FUNC((int32_t)self->machineState->selectedPlane), ObjectModelEntryFlags::none },
{ "stackDepth", OBJECT_MODEL_FUNC((int32_t)self->GetStackDepth()), ObjectModelEntryFlags::none },
{ "state", OBJECT_MODEL_FUNC(self->GetStateText()), ObjectModelEntryFlags::live },
{ "volumetric", OBJECT_MODEL_FUNC((bool)self->machineState->volumetricExtrusion), ObjectModelEntryFlags::none },
};
-constexpr uint8_t GCodeBuffer::objectModelTableDescriptor[] = { 1, 12 };
+constexpr uint8_t GCodeBuffer::objectModelTableDescriptor[] = { 1, 14 + SUPPORT_ASYNC_MOVES };
DEFINE_GET_OBJECT_MODEL_TABLE(GCodeBuffer)
@@ -88,22 +93,26 @@ const char *GCodeBuffer::GetStateText() const noexcept
// Create a default GCodeBuffer
GCodeBuffer::GCodeBuffer(GCodeChannel::RawType channel, GCodeInput *normalIn, FileGCodeInput *fileIn, MessageType mt, Compatibility::RawType c) noexcept
- : codeChannel(channel), normalInput(normalIn),
+ :
+#if SUPPORT_ASYNC_MOVES
+ syncState(SyncState::running),
+#endif
+ printFilePositionAtMacroStart(0),
+ normalInput(normalIn),
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
fileInput(fileIn),
#endif
- responseMessageType(mt), lastResult(GCodeResult::ok),
+ responseMessageType(mt),
#if HAS_SBC_INTERFACE
binaryParser(*this),
#endif
stringParser(*this),
machineState(new GCodeMachineState()), whenReportDueTimerStarted(millis()),
-#if HAS_SBC_INTERFACE
- isBinaryBuffer(false),
-#endif
+ codeChannel(channel), lastResult(GCodeResult::ok),
timerRunning(false), motionCommanded(false)
+
#if HAS_SBC_INTERFACE
- , isWaitingForMacro(false), invalidated(false)
+ , isWaitingForMacro(false), isBinaryBuffer(false), invalidated(false)
#endif
{
mutex.Create(((GCodeChannel)channel).ToString());
@@ -328,6 +337,46 @@ int8_t GCodeBuffer::GetCommandFraction() const noexcept
return PARSER_OPERATION(GetCommandFraction());
}
+#if SUPPORT_ASYNC_MOVES
+
+// Determine whether the other input channel is at a strictly later point than we are
+bool GCodeBuffer::IsLaterThan(const GCodeBuffer& other) const noexcept
+{
+ unsigned int ourDepth = GetStackDepth();
+ unsigned int otherDepth = other.GetStackDepth();
+ const GCodeMachineState *ourState = machineState;
+ const GCodeMachineState *otherState = other.machineState;
+ while (ourDepth > otherDepth)
+ {
+ ourState = ourState->GetPrevious();
+ --ourDepth;
+ }
+ while (otherDepth > ourDepth)
+ {
+ otherState = otherState->GetPrevious();
+ --otherDepth;
+ }
+
+ bool otherIsLater = false;
+ while (ourState != nullptr)
+ {
+ if (otherState->lineNumber > ourState->lineNumber)
+ {
+ otherIsLater = true;
+ }
+ else if (otherState->lineNumber < ourState->lineNumber)
+ {
+ otherIsLater = false;
+ }
+ otherState = otherState->GetPrevious();
+ ourState = ourState->GetPrevious();
+ }
+
+ return otherIsLater;
+}
+
+#endif
+
// Return true if the command we have just completed was the last command in the line of GCode.
// If the command was or called a macro then there will be no command in the buffer, so we must return true for this case also.
bool GCodeBuffer::IsLastCommand() const noexcept
@@ -375,18 +424,43 @@ float GCodeBuffer::GetFValue() THROWS(GCodeException)
return PARSER_OPERATION(GetFValue());
}
+// Get a float after a key letter and check that it is greater then zero
+float GCodeBuffer::GetPositiveFValue() THROWS(GCodeException)
+{
+ const int column =
+#if HAS_SBC_INTERFACE
+ (isBinaryBuffer) ? -1 :
+#endif
+ stringParser.GetColumn();
+ const float val = GetFValue();
+ if (val > 0.0) { return val; }
+ throw GCodeException(GetLineNumber(), column, "value must be greater than zero");
+}
+
+// Get a float after a key letter and check that it is greater than or equal to zero
+float GCodeBuffer::GetNonNegativeFValue() THROWS(GCodeException)
+{
+ const int column =
+#if HAS_SBC_INTERFACE
+ (isBinaryBuffer) ? -1 :
+#endif
+ stringParser.GetColumn();
+ const float val = GetFValue();
+ if (val >= 0.0) { return val; }
+ throw GCodeException(GetLineNumber(), column, "value must be not less than zero");
+}
+
float GCodeBuffer::GetLimitedFValue(char c, float minValue, float maxValue) THROWS(GCodeException)
{
MustSee(c);
+ const int column =
+#if HAS_SBC_INTERFACE
+ (isBinaryBuffer) ? -1 :
+#endif
+ stringParser.GetColumn();
const float ret = GetFValue();
- if (ret < minValue)
- {
- throw GCodeException(GetLineNumber(), -1, "parameter '%c' too low", (uint32_t)c);
- }
- if (ret > maxValue)
- {
- throw GCodeException(GetLineNumber(), -1, "parameter '%c' too high", (uint32_t)c);
- }
+ if (ret < minValue) { throw GCodeException(GetLineNumber(), column, "parameter '%c' too low", (uint32_t)c); }
+ if (ret > maxValue) { throw GCodeException(GetLineNumber(), column, "parameter '%c' too high", (uint32_t)c); }
return ret;
}
@@ -550,6 +624,12 @@ void GCodeBuffer::GetUnsignedArray(uint32_t arr[], size_t& length, bool doPad) T
}
}
+// Get a string array after a key letter
+ExpressionValue GCodeBuffer::GetExpression() THROWS(GCodeException)
+{
+ return PARSER_OPERATION(GetExpression());
+}
+
// Get a :-separated list of drivers after a key letter
void GCodeBuffer::GetDriverIdArray(DriverId arr[], size_t& length)
{
@@ -583,19 +663,24 @@ bool GCodeBuffer::TryGetIValue(char c, int32_t& val, bool& seen) THROWS(GCodeExc
// Try to get a signed integer value, throw if outside limits
bool GCodeBuffer::TryGetLimitedIValue(char c, int32_t& val, bool& seen, int32_t minValue, int32_t maxValue) THROWS(GCodeException)
{
- const bool b = TryGetIValue(c, val, seen);
- if (b)
+ if (Seen(c))
{
- if (val < minValue)
- {
- throw GCodeException(GetLineNumber(), -1, "parameter '%c' too low", (uint32_t)c);
- }
- if (val > maxValue)
- {
- throw GCodeException(GetLineNumber(), -1, "parameter '%c' too high", (uint32_t)c);
- }
+ val = GetLimitedUIValue(c, minValue, maxValue);
+ seen = true;
+ return true;
}
- return b;
+ return false;
+}
+
+bool GCodeBuffer::TryGetNonNegativeFValue(char c, float& val, bool& seen) THROWS(GCodeException)
+{
+ if (Seen(c))
+ {
+ val = GetNonNegativeFValue();
+ seen = true;
+ return true;
+ }
+ return false;
}
// If the specified parameter character is found, fetch 'value' and set 'seen'. Otherwise leave val and seen alone.
@@ -613,12 +698,13 @@ bool GCodeBuffer::TryGetUIValue(char c, uint32_t& val, bool& seen) THROWS(GCodeE
// Try to get an unsigned integer value, throw if >= limit
bool GCodeBuffer::TryGetLimitedUIValue(char c, uint32_t& val, bool& seen, uint32_t maxValuePlusOne) THROWS(GCodeException)
{
- const bool b = TryGetUIValue(c, val, seen);
- if (b && val >= maxValuePlusOne)
+ if (Seen(c))
{
- throw GCodeException(GetLineNumber(), -1, "parameter '%c' too high", (uint32_t)c);
+ val = GetLimitedUIValue(c, maxValuePlusOne);
+ seen = true;
+ return true;
}
- return b;
+ return false;
}
// If the specified parameter character is found, fetch 'value' as a Boolean and set 'seen'. Otherwise leave val and seen alone.
@@ -636,7 +722,7 @@ bool GCodeBuffer::TryGetBValue(char c, bool& val, bool& seen) THROWS(GCodeExcept
// Try to get an int array exactly 'numVals' long after parameter letter 'c'.
// If the wrong number of values is provided, generate an error message and return true.
// Else set 'seen' if we saw the letter and value, and return false.
-bool GCodeBuffer::TryGetUIArray(char c, size_t numVals, uint32_t vals[], const StringRef& reply, bool& seen, bool doPad) THROWS(GCodeException)
+void GCodeBuffer::TryGetUIArray(char c, size_t numVals, uint32_t vals[], bool& seen, bool doPad) THROWS(GCodeException)
{
if (Seen(c))
{
@@ -648,17 +734,15 @@ bool GCodeBuffer::TryGetUIArray(char c, size_t numVals, uint32_t vals[], const S
}
else
{
- reply.printf("Wrong number of values after '\''%c'\'', expected %d", c, numVals);
- return true;
+ ThrowGCodeException("Wrong number of values in array, expected %u", (uint32_t)numVals);
}
}
- return false;
}
// Try to get a float array exactly 'numVals' long after parameter letter 'c'.
// If the wrong number of values is provided, generate an error message and return true.
// Else set 'seen' if we saw the letter and value, and return false.
-bool GCodeBuffer::TryGetFloatArray(char c, size_t numVals, float vals[], const StringRef& reply, bool& seen, bool doPad) THROWS(GCodeException)
+void GCodeBuffer::TryGetFloatArray(char c, size_t numVals, float vals[], bool& seen, bool doPad) THROWS(GCodeException)
{
if (Seen(c))
{
@@ -670,11 +754,9 @@ bool GCodeBuffer::TryGetFloatArray(char c, size_t numVals, float vals[], const S
}
else
{
- reply.printf("Wrong number of values after '\''%c'\'', expected %d", c, numVals);
- return true;
+ ThrowGCodeException("Wrong number of values in array, expected %u", (uint32_t)numVals);
}
}
- return false;
}
// Try to get a quoted string after parameter letter.
@@ -889,6 +971,40 @@ void GCodeBuffer::AbortFile(bool abortAll, bool requestAbort) noexcept
}
}
+// This is called on a fileGCode when we stop a print. It closes the file and re-initialises the buffer.
+void GCodeBuffer::ClosePrintFile() noexcept
+{
+#if HAS_SBC_INTERFACE
+ if (reprap.UsingSbcInterface())
+ {
+ FileId printFileId = OriginalMachineState().fileId;
+ if (printFileId != NoFileId)
+ {
+ for (GCodeMachineState *ms = machineState; ms != nullptr; ms = ms->GetPrevious())
+ {
+ if (ms->fileId == printFileId)
+ {
+ ms->fileId = NoFileId;
+ }
+ }
+ }
+ }
+ else
+#endif
+ {
+#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
+ FileData& fileBeingPrinted = OriginalMachineState().fileState;
+ GetFileInput()->Reset(fileBeingPrinted);
+ if (fileBeingPrinted.IsLive())
+ {
+ fileBeingPrinted.Close();
+ }
+#endif
+ }
+
+ Init();
+}
+
#if HAS_SBC_INTERFACE
void GCodeBuffer::SetFileFinished() noexcept
@@ -939,21 +1055,6 @@ void GCodeBuffer::SetPrintFinished() noexcept
}
}
-void GCodeBuffer::ClosePrintFile() noexcept
-{
- FileId printFileId = OriginalMachineState().fileId;
- if (printFileId != NoFileId)
- {
- for (GCodeMachineState *ms = machineState; ms != nullptr; ms = ms->GetPrevious())
- {
- if (ms->fileId == printFileId)
- {
- ms->fileId = NoFileId;
- }
- }
- }
-}
-
// This is only called when using the SBC interface and returns if the macro file could be opened
bool GCodeBuffer::RequestMacroFile(const char *filename, bool fromCode) noexcept
{
@@ -1012,7 +1113,7 @@ void GCodeBuffer::MacroFileClosed() noexcept
// Tell this input source that any message it sent and is waiting on has been acknowledged
// Allow for the possibility that the source may have started running a macro since it started waiting
-void GCodeBuffer::MessageAcknowledged(bool cancelled) noexcept
+void GCodeBuffer::MessageAcknowledged(bool cancelled, ExpressionValue rslt) noexcept
{
for (GCodeMachineState *ms = machineState; ms != nullptr; ms = ms->GetPrevious())
{
@@ -1021,6 +1122,11 @@ void GCodeBuffer::MessageAcknowledged(bool cancelled) noexcept
ms->waitingForAcknowledgement = false;
ms->messageAcknowledged = true;
ms->messageCancelled = cancelled;
+ m291Result = rslt;
+ if (cancelled)
+ {
+ lastResult = GCodeResult::m291Cancelled;
+ }
#if HAS_SBC_INTERFACE
messageAcknowledged = !cancelled || !ms->DoingFile();
reprap.GetSbcInterface().EventOccurred();
@@ -1040,9 +1146,41 @@ MessageType GCodeBuffer::GetResponseMessageType() const noexcept
return responseMessageType;
}
-FilePosition GCodeBuffer::GetFilePosition() const noexcept
+FilePosition GCodeBuffer::GetJobFilePosition() const noexcept
+{
+ return (IsFileChannel() && !IsDoingFileMacro()) ? PARSER_OPERATION(GetFilePosition()) : noFilePosition;
+}
+
+// Return the current position of the file being printed in bytes.
+// May return noFilePosition if allowNoFilePos is true
+FilePosition GCodeBuffer::GetPrintingFilePosition(bool allowNoFilePos) const noexcept
+{
+#if HAS_SBC_INTERFACE
+ if (!reprap.UsingSbcInterface())
+#endif
+ {
+#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
+ const FileData& fileBeingPrinted = OriginalMachineState().fileState;
+ if (!fileBeingPrinted.IsLive())
+ {
+ return allowNoFilePos ? noFilePosition : 0;
+ }
+#endif
+ }
+
+#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
+ const FilePosition pos = (IsDoingFileMacro())
+ ? printFilePositionAtMacroStart // the position before we started executing the macro
+ : GetJobFilePosition(); // the actual position, allowing for bytes cached but not yet processed
+ return (pos != noFilePosition || allowNoFilePos) ? pos : 0;
+#else
+ return allowNoFilePos ? noFilePosition : 0;
+#endif
+}
+
+void GCodeBuffer::SavePrintingFilePosition() noexcept
{
- return PARSER_OPERATION(GetFilePosition());
+ printFilePositionAtMacroStart = PARSER_OPERATION(GetFilePosition());
}
void GCodeBuffer::WaitForAcknowledgement() noexcept
@@ -1136,6 +1274,26 @@ VariableSet& GCodeBuffer::GetVariables() const noexcept
return mc->variables;
}
+void GCodeBuffer::ThrowGCodeException(const char *msg) const THROWS(GCodeException)
+{
+ const int column =
+#if HAS_SBC_INTERFACE
+ (isBinaryBuffer) ? -1 :
+#endif
+ stringParser.GetColumn();
+ throw GCodeException(GetLineNumber(), column, msg);
+}
+
+void GCodeBuffer::ThrowGCodeException(const char *msg, uint32_t param) const THROWS(GCodeException)
+{
+ const int column =
+#if HAS_SBC_INTERFACE
+ (isBinaryBuffer) ? -1 :
+#endif
+ stringParser.GetColumn();
+ throw GCodeException(GetLineNumber(), column, msg, param);
+}
+
#if SUPPORT_COORDINATE_ROTATION
bool GCodeBuffer::DoingCoordinateRotation() const noexcept
diff --git a/src/GCodes/GCodeBuffer/GCodeBuffer.h b/src/GCodes/GCodeBuffer/GCodeBuffer.h
index 77afefba..4fc40cbc 100644
--- a/src/GCodes/GCodeBuffer/GCodeBuffer.h
+++ b/src/GCodes/GCodeBuffer/GCodeBuffer.h
@@ -14,10 +14,11 @@
#include <RepRapFirmware.h>
#include <GCodes/GCodeChannel.h>
#include <GCodes/GCodeMachineState.h>
+#include <ObjectModel/ObjectModel.h>
+
#if HAS_SBC_INTERFACE
# include <SBC/SbcMessageFormats.h>
#endif
-#include <ObjectModel/ObjectModel.h>
class FileGCodeInput;
@@ -70,6 +71,7 @@ public:
bool IsLastCommand() const noexcept;
GCodeResult GetLastResult() const noexcept { return lastResult; }
void SetLastResult(GCodeResult r) noexcept { lastResult = r; }
+ ExpressionValue GetM291Result() const noexcept { return m291Result; }
bool Seen(char c) noexcept SPEED_CRITICAL; // Is a character present?
void MustSee(char c) THROWS(GCodeException); // Test for character present, throw error if not
@@ -77,6 +79,8 @@ public:
inline bool SeenAny(const char *s) const noexcept { return SeenAny(Bitmap<uint32_t>(ParametersToBitmap(s))); }
float GetFValue() THROWS(GCodeException) SPEED_CRITICAL; // Get a float after a key letter
+ float GetPositiveFValue() THROWS(GCodeException) SPEED_CRITICAL; // Get a float after a key letter and check that it is greater than zero
+ float GetNonNegativeFValue() THROWS(GCodeException) SPEED_CRITICAL; // Get a float after a key letter and check that it is greater than or equal to zero
float GetDistance() THROWS(GCodeException); // Get a distance or coordinate and convert it from inches to mm if necessary
float GetSpeed() THROWS(GCodeException); // Get a speed in mm/min or inches/min and convert it to mm/step_clock
float GetSpeedFromMm(bool useSeconds) THROWS(GCodeException); // Get a speed in mm/min or optionally /sec and convert it to mm/step_clock
@@ -107,15 +111,17 @@ public:
void GetIntArray(int32_t arr[], size_t& length, bool doPad) THROWS(GCodeException); // Get a :-separated list of ints after a key letter
void GetUnsignedArray(uint32_t arr[], size_t& length, bool doPad) THROWS(GCodeException); // Get a :-separated list of unsigned ints after a key letter
void GetDriverIdArray(DriverId arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of drivers after a key letter
+ ExpressionValue GetExpression() THROWS(GCodeException); // Get a general expression after a key letter
bool TryGetFValue(char c, float& val, bool& seen) THROWS(GCodeException);
bool TryGetIValue(char c, int32_t& val, bool& seen) THROWS(GCodeException);
bool TryGetLimitedIValue(char c, int32_t& val, bool& seen, int32_t minValue, int32_t maxValue) THROWS(GCodeException);
bool TryGetUIValue(char c, uint32_t& val, bool& seen) THROWS(GCodeException);
bool TryGetLimitedUIValue(char c, uint32_t& val, bool& seen, uint32_t maxValuePlusOne) THROWS(GCodeException);
+ bool TryGetNonNegativeFValue(char c, float& val, bool& seen) THROWS(GCodeException);
bool TryGetBValue(char c, bool& val, bool& seen) THROWS(GCodeException);
- bool TryGetFloatArray(char c, size_t numVals, float vals[], const StringRef& reply, bool& seen, bool doPad = false) THROWS(GCodeException);
- bool TryGetUIArray(char c, size_t numVals, uint32_t vals[], const StringRef& reply, bool& seen, bool doPad = false) THROWS(GCodeException);
+ void TryGetFloatArray(char c, size_t numVals, float vals[], bool& seen, bool doPad = false) THROWS(GCodeException);
+ void TryGetUIArray(char c, size_t numVals, uint32_t vals[], bool& seen, bool doPad = false) THROWS(GCodeException);
bool TryGetQuotedString(char c, const StringRef& str, bool& seen, bool allowEmpty = false) THROWS(GCodeException);
bool TryGetPossiblyQuotedString(char c, const StringRef& str, bool& seen) THROWS(GCodeException);
@@ -125,6 +131,25 @@ public:
bool IsExecuting() const noexcept; // Return true if a gcode has been started and is not paused
void SetFinished(bool f) noexcept; // Set the G Code executed (or not)
+ size_t GetActiveQueueNumber() const noexcept // Get the movement queue number that this buffer uses
+ {
+#if SUPPORT_ASYNC_MOVES
+ return machineState->GetCommandedQueue();
+#else
+ return 0;
+#endif
+ }
+
+#if SUPPORT_ASYNC_MOVES
+ void SetActiveQueueNumber(size_t qn) noexcept { machineState->SetCommandedQueue(qn); }
+ void ExecuteOnlyQueue(size_t qn) noexcept { machineState->ExecuteOnly(qn); }
+ size_t GetOwnQueueNumber() const noexcept { return machineState->GetOwnQueue(); }
+ void ExecuteAll() noexcept { machineState->ExecuteAll(); }
+ bool Executing() const noexcept { return machineState->Executing(); } // Return true if this GCodeBuffer for executing commands addressed to the current queue
+ bool ExecutingAll() const noexcept { return machineState->ExecutingAll(); }
+ size_t GetQueueNumberToLock() const noexcept { return machineState->GetQueueNumberToLock(); }
+#endif
+
void SetCommsProperties(uint32_t arg) noexcept;
GCodeMachineState& LatestMachineState() const noexcept { return *machineState; }
@@ -148,9 +173,12 @@ public:
bool IsDoingFile() const noexcept; // Return true if this source is executing a file
bool IsDoingLocalFile() const noexcept; // Return true if this source is executing a file from the local SD card
bool IsDoingFileMacro() const noexcept; // Return true if this source is executing a file macro
- FilePosition GetFilePosition() const noexcept; // Get the file position at the start of the current command
+ FilePosition GetJobFilePosition() const noexcept; // Get the file position at the start of the current command
+ FilePosition GetPrintingFilePosition(bool allowNoFilePos) const noexcept; // Get the file position in the printing file
+ void SavePrintingFilePosition() noexcept;
void WaitForAcknowledgement() noexcept; // Flag that we are waiting for acknowledgement
+ void ClosePrintFile() noexcept; // Close the print file
#if HAS_SBC_INTERFACE
bool IsBinary() const noexcept { return isBinaryBuffer; } // Return true if the code is in binary format
@@ -158,7 +186,6 @@ public:
bool IsFileFinished() const noexcept; // Return true if this source has finished execution of a file
void SetFileFinished() noexcept; // Mark the current file as finished
void SetPrintFinished() noexcept; // Mark the current print file as finished
- void ClosePrintFile() noexcept; // Close the print file
bool RequestMacroFile(const char *filename, bool fromCode) noexcept; // Request execution of a file macro
volatile bool IsWaitingForMacro() const noexcept { return isWaitingForMacro; } // Indicates if the GB is waiting for a macro to be opened
@@ -194,9 +221,17 @@ public:
void SetState(GCodeState newState) noexcept;
void SetState(GCodeState newState, uint16_t param) noexcept;
void AdvanceState() noexcept;
- void MessageAcknowledged(bool cancelled) noexcept;
+ void MessageAcknowledged(bool cancelled, ExpressionValue rslt) noexcept;
GCodeChannel GetChannel() const noexcept { return codeChannel; }
+ bool IsFileChannel() const noexcept
+ {
+ return codeChannel == GCodeChannel::File
+#if SUPPORT_ASYNC_MOVES
+ || codeChannel == GCodeChannel::File2
+#endif
+ ;
+ }
const char *GetIdentity() const noexcept { return codeChannel.ToString(); }
bool CanQueueCodes() const noexcept;
MessageType GetResponseMessageType() const noexcept;
@@ -241,12 +276,24 @@ public:
void AddParameters(VariableSet& vars, int codeRunning) noexcept;
VariableSet& GetVariables() const noexcept;
+ [[noreturn]] void ThrowGCodeException(const char *msg) const THROWS(GCodeException);
+ [[noreturn]] void ThrowGCodeException(const char *msg, uint32_t param) const THROWS(GCodeException);
+
#if SUPPORT_COORDINATE_ROTATION
bool DoingCoordinateRotation() const noexcept;
#endif
+#if SUPPORT_ASYNC_MOVES
+ bool IsLaterThan(const GCodeBuffer& other) const noexcept;
+#endif
+
Mutex mutex;
+#if SUPPORT_ASYNC_MOVES
+ enum class SyncState { running, syncing, synced } ;
+ SyncState syncState;
+#endif
+
protected:
DECLARE_OBJECT_MODEL
@@ -265,7 +312,7 @@ private:
const char *GetStateText() const noexcept;
#endif
- const GCodeChannel codeChannel; // Channel number of this instance
+ FilePosition printFilePositionAtMacroStart; // the saved file position when we started executing a macro
GCodeInput *normalInput; // Our normal input stream, or nullptr if there isn't one
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
@@ -274,25 +321,24 @@ private:
const MessageType responseMessageType; // The message type we use for responses to string codes coming from this channel
- GCodeResult lastResult;
-
#if HAS_SBC_INTERFACE
BinaryParser binaryParser;
#endif
StringParser stringParser;
- GCodeBufferState bufferState; // Idle, executing or paused
GCodeMachineState *machineState; // Machine state for this gcode source
+ ExpressionValue m291Result; // the value entered or choice selected in response to a M291 command
uint32_t whenTimerStarted; // When we started waiting
uint32_t whenReportDueTimerStarted; // When the report-due-timer has been started
static constexpr uint32_t reportDueInterval = 1000; // Interval in which we send in ms
-#if HAS_SBC_INTERFACE
- bool isBinaryBuffer;
-#endif
- bool timerRunning; // True if we are waiting
+ const GCodeChannel codeChannel; // Channel number of this instance
+ GCodeBufferState bufferState; // Idle, executing or paused
+ GCodeResult lastResult;
+
+ bool timerRunning; // true if we are waiting
bool motionCommanded; // true if this GCode stream has commanded motion since it last waited for motion to stop
alignas(4) char buffer[MaxGCodeLength]; // must be aligned because in SBC binary mode we do dword fetches from it
@@ -307,6 +353,7 @@ private:
// Accessed only when the GB mutex is acquired
String<MaxFilenameLength> requestedMacroFile;
+ bool isBinaryBuffer;
uint8_t
macroJustStarted : 1, // Whether the GB has just started a macro file
macroFileError : 1, // Whether the macro file could be opened or if an error occurred
diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp
index 75113d42..a57ef337 100644
--- a/src/GCodes/GCodeBuffer/StringParser.cpp
+++ b/src/GCodes/GCodeBuffer/StringParser.cpp
@@ -1075,7 +1075,9 @@ void StringParser::SetFinished() noexcept
}
}
-// Get the file position at the start of the current command
+// Get the file position at the start of the current command.
+// This is called to get the file position within the current job file so that we can save the position in the DDAs to facilitate pause and resume.
+// It is also called to get the position within a macro file when executing a while loop in that file.
FilePosition StringParser::GetFilePosition() const noexcept
{
#if HAS_MASS_STORAGE
@@ -1319,6 +1321,17 @@ void StringParser::GetDriverIdArray(DriverId arr[], size_t& returnedLength) THRO
readPointer = -1;
}
+// Get a :-separated list of strings after a key letter
+ExpressionValue StringParser::GetExpression() THROWS(GCodeException)
+{
+ if (gb.buffer[readPointer] == '{')
+ {
+ ExpressionParser parser(gb, gb.buffer + readPointer, gb.buffer + ARRAY_SIZE(gb.buffer), commandIndent + readPointer);
+ return parser.Parse();
+ }
+ throw ConstructParseException("expected an expression inside { }");
+}
+
void StringParser::CheckArrayLength(size_t actualLength, size_t maxLength) THROWS(GCodeException)
{
if (actualLength >= maxLength)
@@ -1352,12 +1365,12 @@ void StringParser::GetQuotedString(const StringRef& str, bool allowEmpty) THROWS
break;
default:
- throw ConstructParseException("expected string expression");
+ throw ConstructParseException("expected a string expression");
}
if (!allowEmpty && str.IsEmpty())
{
- throw ConstructParseException("non-empty string expected");
+ throw ConstructParseException("expected a non-empty string");
}
}
@@ -1412,7 +1425,7 @@ void StringParser::GetPossiblyQuotedString(const StringRef& str, bool allowEmpty
InternalGetPossiblyQuotedString(str);
if (!allowEmpty && str.IsEmpty())
{
- throw ConstructParseException("non-empty string expected");
+ throw ConstructParseException("expected a non-empty string");
}
}
@@ -1456,7 +1469,7 @@ void StringParser::GetUnprecedentedString(const StringRef& str, bool allowEmpty)
InternalGetPossiblyQuotedString(str);
if (!allowEmpty && str.IsEmpty())
{
- throw ConstructParseException("non-empty string expected");
+ throw ConstructParseException("expected a non-empty string");
}
}
@@ -1940,17 +1953,23 @@ void StringParser::AddParameters(VariableSet& vs, int codeRunning) noexcept
GCodeException StringParser::ConstructParseException(const char *str) const noexcept
{
- return GCodeException(gb.GetLineNumber(), readPointer + commandIndent, str);
+ return GCodeException(gb.GetLineNumber(), GetColumn(), str);
}
GCodeException StringParser::ConstructParseException(const char *str, const char *param) const noexcept
{
- return GCodeException(gb.GetLineNumber(), readPointer + commandIndent, str, param);
+ return GCodeException(gb.GetLineNumber(), GetColumn(), str, param);
}
GCodeException StringParser::ConstructParseException(const char *str, uint32_t param) const noexcept
{
- return GCodeException(gb.GetLineNumber(), readPointer + commandIndent, str, param);
+ return GCodeException(gb.GetLineNumber(), GetColumn(), str, param);
+}
+
+// Get the current column if we can, else return -1
+int StringParser::GetColumn() const noexcept
+{
+ return (readPointer < 0) ? -1 : readPointer + commandIndent;
}
// End
diff --git a/src/GCodes/GCodeBuffer/StringParser.h b/src/GCodes/GCodeBuffer/StringParser.h
index 9bb6ba60..5c9953f8 100644
--- a/src/GCodes/GCodeBuffer/StringParser.h
+++ b/src/GCodes/GCodeBuffer/StringParser.h
@@ -61,6 +61,7 @@ public:
void GetIntArray(int32_t arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of ints after a key letter
void GetUnsignedArray(uint32_t arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of unsigned ints after a key letter
void GetDriverIdArray(DriverId arr[], size_t& length) THROWS(GCodeException); // Get a :-separated list of drivers after a key letter
+ ExpressionValue GetExpression() THROWS(GCodeException); // Get an expression after a key letter
void ResetIndentation() noexcept; // Reset the indentation level to the last one
void SetFinished() noexcept; // Set the G Code finished
@@ -90,6 +91,8 @@ public:
GCodeException ConstructParseException(const char *str, const char *param) const noexcept;
GCodeException ConstructParseException(const char *str, uint32_t param) const noexcept;
+ int GetColumn() const noexcept; // Get the current column if we can, else return -1
+
private:
GCodeBuffer& gb;
diff --git a/src/GCodes/GCodeChannel.h b/src/GCodes/GCodeChannel.h
index 7ea8434a..66ae89b5 100644
--- a/src/GCodes/GCodeChannel.h
+++ b/src/GCodes/GCodeChannel.h
@@ -17,7 +17,7 @@
# undef USB
#endif
-NamedEnum(GCodeChannel, uint8_t, HTTP, Telnet, File, USB, Aux, Trigger, Queue, LCD, SBC, Daemon, Aux2, Autopause);
+NamedEnum(GCodeChannel, uint8_t, HTTP, Telnet, File, USB, Aux, Trigger, Queue, LCD, SBC, Daemon, Aux2, Autopause, File2, Queue2);
constexpr size_t NumGCodeChannels = GCodeChannel::NumValues;
diff --git a/src/GCodes/GCodeException.cpp b/src/GCodes/GCodeException.cpp
index a11ef584..9789ac52 100644
--- a/src/GCodes/GCodeException.cpp
+++ b/src/GCodes/GCodeException.cpp
@@ -11,7 +11,7 @@
#include <GCodes/GCodeBuffer/GCodeBuffer.h>
// Construct the error message. This will be prefixed with "Error: " when it is returned to the user.
-void GCodeException::GetMessage(const StringRef &reply, const GCodeBuffer *gb) const noexcept
+void GCodeException::GetMessage(const StringRef &reply, const GCodeBuffer *null gb) const noexcept
{
// Print the file location, if possible
const bool inFile = gb != nullptr && gb->IsDoingFile();
@@ -54,14 +54,22 @@ void GCodeException::GetMessage(const StringRef &reply, const GCodeBuffer *gb) c
}
// Print the message and any parameter
- if (haveStringParam)
+ if (message == nullptr)
+ {
+ reply.cat("<null error message>"); // should not happem
+ }
+ else if (strstr(message, "%s"))
{
reply.catf(message, stringParam.c_str());
}
- else
+ else if (strstr(message, "%u"))
{
reply.catf(message, param.u);
}
+ else
+ {
+ reply.catf(message, param.i);
+ }
}
// End
diff --git a/src/GCodes/GCodeException.h b/src/GCodes/GCodeException.h
index c82f4074..d72ab941 100644
--- a/src/GCodes/GCodeException.h
+++ b/src/GCodes/GCodeException.h
@@ -16,29 +16,35 @@ namespace StackUsage
constexpr uint32_t Margin = 300; // the margin we allow for calls to non-recursive functions that can throw
}
+// This class is mostly used to throw exceptions when processing GCode. It is also used to store error messages that need to be retrieved later.
+// Field "message" should always point to a constant string in flash memory, or be null.
+// The error message may have a string, int32_t or uint32_t parameter
class GCodeException
{
public:
- explicit GCodeException(const char *_ecv_array msg) noexcept: line(-1), column(-1), message(msg), haveStringParam(false) { }
+ GCodeException() noexcept : line(-1), column(-1), message(nullptr) { }
+ explicit GCodeException(const char *_ecv_array msg) noexcept: line(-1), column(-1), message(msg) { }
- GCodeException(int lin, int col, const char *_ecv_array msg) noexcept : line(lin), column(col), message(msg), haveStringParam(false) { }
+ GCodeException(int lin, int col, const char *_ecv_array msg) noexcept : line(lin), column(col), message(msg) { }
- GCodeException(int lin, int col, const char *_ecv_array msg, const char *_ecv_array sparam) noexcept : line(lin), column(col), message(msg), haveStringParam(true)
+ GCodeException(int lin, int col, const char *_ecv_array msg, const char *_ecv_array sparam) noexcept : line(lin), column(col), message(msg)
{
stringParam.copy(sparam);
}
- GCodeException(int lin, int col, const char *_ecv_array msg, uint32_t uparam) noexcept : line(lin), column(col), message(msg), haveStringParam(false)
+ GCodeException(int lin, int col, const char *_ecv_array msg, uint32_t uparam) noexcept : line(lin), column(col), message(msg)
{
param.u = uparam;
}
- GCodeException(int lin, int col, const char *_ecv_array msg, int32_t iparam) noexcept : line(lin), column(col), message(msg), haveStringParam(false)
+ GCodeException(int lin, int col, const char *_ecv_array msg, int32_t iparam) noexcept : line(lin), column(col), message(msg)
{
param.i = iparam;
}
- void GetMessage(const StringRef& reply, const GCodeBuffer *gb) const noexcept;
+ void GetMessage(const StringRef& reply, const GCodeBuffer *null gb) const noexcept;
+
+ bool IsNull() const noexcept { return message == nullptr; }
private:
int line;
@@ -49,7 +55,6 @@ private:
int32_t i;
uint32_t u;
} param;
- bool haveStringParam;
String<StringLength50> stringParam;
};
diff --git a/src/GCodes/GCodeMachineState.cpp b/src/GCodes/GCodeMachineState.cpp
index ad02bdd8..304ddc7e 100644
--- a/src/GCodes/GCodeMachineState.cpp
+++ b/src/GCodes/GCodeMachineState.cpp
@@ -20,13 +20,17 @@ GCodeMachineState::GCodeMachineState() noexcept
selectedPlane(0), drivesRelative(false), axesRelative(false),
doingFileMacro(false), waitWhileCooling(false), runningM501(false), runningM502(false),
volumetricExtrusion(false), g53Active(false), runningSystemMacro(false), usingInches(false),
- waitingForAcknowledgement(false), messageAcknowledged(false), localPush(false), macroRestartable(false), firstCommandAfterRestart(false), commandRepeated(false),
+ waitingForAcknowledgement(false), messageAcknowledged(false), localPush(false), macroRestartable(false), firstCommandAfterRestart(false), commandRepeated(false), inverseTimeMode(false),
#if HAS_SBC_INTERFACE
lastCodeFromSbc(false), macroStartedByCode(false), fileFinished(false),
#endif
+ stateParameter(0),
compatibility(Compatibility::RepRapFirmware),
previous(nullptr), errorMessage(nullptr),
blockNesting(0), state(GCodeState::normal), stateMachineResult(GCodeResult::ok)
+#if SUPPORT_ASYNC_MOVES
+ , commandedQueueNumber(0), ownQueueNumber(0), executeAllCommands(true)
+#endif
{
blockStates[0].SetPlainBlock(0);
}
@@ -46,12 +50,17 @@ GCodeMachineState::GCodeMachineState(GCodeMachineState& prev, bool withinSameFil
doingFileMacro(prev.doingFileMacro), waitWhileCooling(prev.waitWhileCooling), runningM501(prev.runningM501), runningM502(prev.runningM502),
volumetricExtrusion(false), g53Active(false), runningSystemMacro(prev.runningSystemMacro), usingInches(prev.usingInches),
waitingForAcknowledgement(false), messageAcknowledged(false), localPush(withinSameFile), firstCommandAfterRestart(prev.firstCommandAfterRestart), commandRepeated(false),
+ inverseTimeMode(withinSameFile && prev.inverseTimeMode),
#if HAS_SBC_INTERFACE
lastCodeFromSbc(prev.lastCodeFromSbc), macroStartedByCode(prev.macroStartedByCode), fileFinished(prev.fileFinished),
#endif
compatibility(prev.compatibility),
previous(&prev), errorMessage(nullptr),
- blockNesting((withinSameFile) ? prev.blockNesting : 0), state(GCodeState::normal), stateMachineResult(GCodeResult::ok)
+ blockNesting((withinSameFile) ? prev.blockNesting : 0),
+ state(GCodeState::normal), stateMachineResult(GCodeResult::ok)
+#if SUPPORT_ASYNC_MOVES
+ , commandedQueueNumber(prev.commandedQueueNumber), ownQueueNumber(prev.ownQueueNumber), executeAllCommands(prev.executeAllCommands)
+#endif
{
if (withinSameFile)
{
@@ -167,6 +176,7 @@ void GCodeMachineState::WaitForAcknowledgement() noexcept
#endif
}
+// This is called only after running config.g and when using M26/M23 to resume a print
void GCodeMachineState::CopyStateFrom(const GCodeMachineState& other) noexcept
{
selectedPlane = other.selectedPlane;
@@ -175,6 +185,7 @@ void GCodeMachineState::CopyStateFrom(const GCodeMachineState& other) noexcept
feedRate = other.feedRate;
volumetricExtrusion = other.volumetricExtrusion;
usingInches = other.usingInches;
+ inverseTimeMode = other.inverseTimeMode;
}
// Set the error message and associated state
@@ -182,7 +193,16 @@ void GCodeMachineState::SetError(const char *msg) noexcept
{
if (stateMachineResult != GCodeResult::error)
{
- errorMessage = msg;
+ errorMessage = GCodeException(msg);
+ stateMachineResult = GCodeResult::error;
+ }
+}
+
+void GCodeMachineState::SetError(const GCodeException& exc) noexcept
+{
+ if (stateMachineResult != GCodeResult::error)
+ {
+ errorMessage = exc;
stateMachineResult = GCodeResult::error;
}
}
@@ -191,20 +211,20 @@ void GCodeMachineState::SetWarning(const char *msg) noexcept
{
if (stateMachineResult == GCodeResult::ok)
{
- errorMessage = msg;
+ errorMessage = GCodeException(msg);
stateMachineResult = GCodeResult::warning;
}
}
// Retrieve the result and error message if it is worse than the one we already have
-void GCodeMachineState::RetrieveStateMachineResult(GCodeResult& rslt, const StringRef& reply) const noexcept
+void GCodeMachineState::RetrieveStateMachineResult(const GCodeBuffer& gb, const StringRef& reply, GCodeResult& rslt) const noexcept
{
if (stateMachineResult >= rslt)
{
rslt = stateMachineResult;
- if (errorMessage != nullptr)
+ if (!errorMessage.IsNull())
{
- reply.copy(errorMessage);
+ errorMessage.GetMessage(reply, &gb);
}
}
}
@@ -212,7 +232,7 @@ void GCodeMachineState::RetrieveStateMachineResult(GCodeResult& rslt, const Stri
GCodeMachineState *GCodeMachineState::Pop() const noexcept
{
GCodeMachineState * const rslt = GetPrevious();
- if (errorMessage != nullptr)
+ if (!errorMessage.IsNull())
{
rslt->errorMessage = errorMessage;
rslt->stateMachineResult = stateMachineResult;
@@ -225,7 +245,7 @@ void GCodeMachineState::SetState(GCodeState newState) noexcept
if (state == GCodeState::normal && newState != GCodeState::normal)
{
stateMachineResult = GCodeResult::ok;
- errorMessage = nullptr;
+ errorMessage = GCodeException();
}
state = newState;
}
diff --git a/src/GCodes/GCodeMachineState.h b/src/GCodes/GCodeMachineState.h
index d5b74a73..0f85869a 100644
--- a/src/GCodes/GCodeMachineState.h
+++ b/src/GCodes/GCodeMachineState.h
@@ -174,7 +174,7 @@ public:
private:
FilePosition fpos; // the file offset at which the current block started
uint32_t lineNumber; // the line number at which the current block started
- uint32_t iterationsDone;
+ uint32_t iterationsDone; // the number of iterations completed of the innermost while-loop
uint16_t indentLevel; // the indentation of this block
BlockType blockType; // the type of this block
};
@@ -230,7 +230,8 @@ public:
localPush : 1, // true if this stack frame was created by M120, so we use the parent variables
macroRestartable : 1, // true if the current macro has used M98 R1 to say that it can be interrupted and restarted
firstCommandAfterRestart : 1, // true if this is the first command after restarting a macro that was interrupted
- commandRepeated : 1 // true if the current command is being repeated because it returned GCodeResult::notFinished the first time
+ commandRepeated : 1, // true if the current command is being repeated because it returned GCodeResult::notFinished the first time
+ inverseTimeMode : 1 // true if using inverse time mode
#if HAS_SBC_INTERFACE
, lastCodeFromSbc : 1,
macroStartedByCode : 1,
@@ -238,8 +239,19 @@ public:
#endif
;
- Compatibility compatibility;
uint16_t stateParameter; // a parameter, the meaning of which depends on what state we are in
+ Compatibility compatibility; // which firmware we are emulating
+
+#if SUPPORT_ASYNC_MOVES
+ void SetCommandedQueue(size_t qn) noexcept { commandedQueueNumber = qn; }
+ size_t GetCommandedQueue() const noexcept { return commandedQueueNumber; }
+ bool Executing() const noexcept { return executeAllCommands || commandedQueueNumber == ownQueueNumber; }
+ void ExecuteAll() noexcept { executeAllCommands = true; }
+ void ExecuteOnly(size_t qn) noexcept { ownQueueNumber = qn; executeAllCommands = false; }
+ size_t GetOwnQueue() const noexcept { return ownQueueNumber; }
+ bool ExecutingAll() const noexcept { return executeAllCommands; }
+ size_t GetQueueNumberToLock() const noexcept { return (executeAllCommands) ? commandedQueueNumber : ownQueueNumber; }
+#endif
bool DoingFile() const noexcept;
void CloseFile() noexcept;
@@ -254,8 +266,9 @@ public:
// Set the error message and associated state
void SetError(const char *msg) noexcept;
+ void SetError(const GCodeException& exc) noexcept;
void SetWarning(const char *msg) noexcept;
- void RetrieveStateMachineResult(GCodeResult& rslt, const StringRef& reply) const noexcept;
+ void RetrieveStateMachineResult(const GCodeBuffer& gb, const StringRef& reply, GCodeResult& rslt) const noexcept;
// Copy values that may have been altered into this state record
// Called after running config.g and after running resurrect.g
@@ -270,10 +283,17 @@ public:
private:
GCodeMachineState *previous;
- const char *errorMessage;
+ GCodeException errorMessage; // we use a GCodeException to store a possible message and a parameter
uint8_t blockNesting;
GCodeState state;
GCodeResult stateMachineResult; // the worst status (ok, warning or error) that we encountered while running the state machine
+
+#if SUPPORT_ASYNC_MOVES
+ uint8_t commandedQueueNumber : 3, // the queue number that was most recently commanded on this channel
+ ownQueueNumber : 3, // the fixed queue number that we use, if executeAllCommands is clear
+ spare : 1,
+ executeAllCommands : 1; // whether to only execute all commands, or only when commandedQueueNumber == fixedQueueNumber
+#endif
};
#endif /* SRC_GCODES_GCODEMACHINESTATE_H_ */
diff --git a/src/GCodes/GCodeQueue.cpp b/src/GCodes/GCodeQueue.cpp
index c3cb25c2..445ebf59 100644
--- a/src/GCodes/GCodeQueue.cpp
+++ b/src/GCodes/GCodeQueue.cpp
@@ -23,92 +23,72 @@ GCodeQueue::GCodeQueue() noexcept : freeItems(nullptr), queuedItems(nullptr)
}
}
-// Return true if the move in the GCodeBuffer should be queued. Caller has already checked that the command does not contain an expression.
-/*static*/ bool GCodeQueue::ShouldQueueCode(GCodeBuffer &gb) THROWS(GCodeException)
+// Return true if the GCode in the GCodeBuffer should be queued
+// Caller has already checked that the command does not contain an expression and involves modifying tool temperatures or spindle speed
+/*static*/ bool GCodeQueue::ShouldQueueG10(GCodeBuffer &gb) noexcept
{
-#if SUPPORT_ROLAND
- // Don't queue codes if the Roland module is active
- if (reprap.GetRoland()->Active())
- {
- return false;
- }
-#endif
+ return reprap.GetMove().GetScheduledMoves() != reprap.GetMove().GetCompletedMoves()
+ && gb.DataLength() <= BufferSizePerQueueItem; // only queue it if it is short enough to fit in a queue item
+}
+// Return true if the MCode in the GCodeBuffer should be queued. Caller has already checked that the command does not contain an expression.
+/*static*/ bool GCodeQueue::ShouldQueueMCode(GCodeBuffer &gb) THROWS(GCodeException)
+{
// Don't queue anything if no moves are being performed
- const uint32_t scheduledMoves = reprap.GetMove().GetScheduledMoves();
- if (scheduledMoves != reprap.GetMove().GetCompletedMoves())
+ if (reprap.GetMove().GetScheduledMoves() != reprap.GetMove().GetCompletedMoves())
{
bool shouldQueue;
- switch (gb.GetCommandLetter())
+ switch (gb.GetCommandNumber())
{
- case 'G':
- shouldQueue = gb.GetCommandNumber() == 10
- && gb.Seen('P')
- && !gb.Seen('L')
- && (gb.Seen('R') || gb.Seen('S')); // set active/standby temperatures
+ case 3: // spindle or laser control
+ case 5: // spindle or laser control
+ // On laser devices we use these codes to set the default laser power for the next G1 command
+ shouldQueue = reprap.GetGCodes().GetMachineType() != MachineType::laser;
break;
- case 'M':
- {
- switch (gb.GetCommandNumber())
- {
- case 3: // spindle or laser control
- case 5: // spindle or laser control
- // On laser devices we use these codes to set the default laser power for the next G1 command
- shouldQueue = reprap.GetGCodes().GetMachineType() != MachineType::laser;
- break;
-
- case 4: // spindle control
- case 42: // set IO pin
- case 106: // fan control
- case 107: // fan off
- case 104: // set temperatures and return immediately
- case 140: // set bed temperature and return immediately
- case 141: // set chamber temperature and return immediately
- case 144: // bed standby
- case 280: // set servo
- case 300: // beep
- case 568: // spindle or temperature control
- shouldQueue = true;
- break;
-
- case 117: // display message
- {
- // We need to call GetUnprecedentedString to ensure that if the string argument is not quoted, gb.DataLength() will return the correct value.
- // We need to pass the correct length string buffer here because GetUnprecedentedString will throw if the string is too long for the buffer.
- String<M117StringLength> dummy;
- gb.GetUnprecedentedString(dummy.GetRef());
- }
- shouldQueue = true;
- break;
+ case 4: // spindle control
+ case 42: // set IO pin
+ case 106: // fan control
+ case 107: // fan off
+ case 104: // set temperatures and return immediately
+ case 140: // set bed temperature and return immediately
+ case 141: // set chamber temperature and return immediately
+ case 144: // bed standby
+ case 280: // set servo
+ case 300: // beep
+ case 568: // spindle or temperature control
+ shouldQueue = true;
+ break;
-#if SUPPORT_LED_STRIPS
- case 150: // set LED colours
- shouldQueue = !LedStripDriver::MustStopMovement(gb); // if it is going to call LockMovementAndWaitForStandstill then we mustn't queue it
- break;
-#endif
- // A note about M291:
- // - We cannot queue M291 messages that are blocking, i.e. with S2 or S3 parameter
- // - If we queue non-blocking M291 messages then it can happen that if a non-blocking M291 is used and a little later a blocking M291 is used,
- // then the blocking one gets displayed while the non-blocking one is still in the queue. Then the non-blocking one overwrites it, and the
- // blocking one can no longer be acknowledged except by sending M292 manually.
- // - Therefore we no longer queue any M291 commands.
- case 291:
- default:
- shouldQueue = false;
- break;
- }
+ case 117: // display message
+ {
+ // We need to call GetUnprecedentedString to ensure that if the string argument is not quoted, gb.DataLength() will return the correct value.
+ // We need to pass the correct length string buffer here because GetUnprecedentedString will throw if the string is too long for the buffer.
+ String<M117StringLength> dummy;
+ gb.GetUnprecedentedString(dummy.GetRef());
}
+ shouldQueue = true;
break;
- default:
- shouldQueue = false;
+#if SUPPORT_LED_STRIPS
+ case 150: // set LED colours
+ shouldQueue = !LedStripDriver::MustStopMovement(gb); // if it is going to call LockMovementAndWaitForStandstill then we mustn't queue it
break;
+#endif
+ // A note about M291:
+ // - We cannot queue M291 messages that are blocking, i.e. with S2 or S3 parameter
+ // - If we queue non-blocking M291 messages then it can happen that if a non-blocking M291 is used and a little later a blocking M291 is used,
+ // then the blocking one gets displayed while the non-blocking one is still in the queue. Then the non-blocking one overwrites it, and the
+ // blocking one can no longer be acknowledged except by sending M292 manually.
+ // - Therefore we no longer queue any M291 commands.
+ case 291:
+ default:
+ return false;
}
- if (shouldQueue)
+ if (shouldQueue && gb.DataLength() <= BufferSizePerQueueItem) // only queue it if it is short enough to fit in a queue item
{
- return gb.DataLength() <= BufferSizePerQueueItem; // only queue it if it is short enough to fit in a queue item
+ return true;
}
}
@@ -118,7 +98,7 @@ GCodeQueue::GCodeQueue() noexcept : freeItems(nullptr), queuedItems(nullptr)
// Try to queue the command in the passed GCodeBuffer.
// If successful, return true to indicate it has been queued.
// If the queue is full, return false. Caller will wait for space to become available.
-bool GCodeQueue::QueueCode(GCodeBuffer &gb, uint32_t scheduleAt) noexcept
+bool GCodeQueue::QueueCode(const GCodeBuffer &gb) noexcept
{
// Can we queue this code somewhere?
if (freeItems == nullptr)
@@ -130,7 +110,7 @@ bool GCodeQueue::QueueCode(GCodeBuffer &gb, uint32_t scheduleAt) noexcept
QueuedCode * const code = freeItems;
freeItems = code->next;
code->AssignFrom(gb);
- code->executeAtMove = scheduleAt;
+ code->executeAtMove = reprap.GetMove().GetScheduledMoves();
code->next = nullptr;
// Append it to the list of queued codes
@@ -232,11 +212,11 @@ void GCodeQueue::Clear() noexcept
}
}
-void GCodeQueue::Diagnostics(MessageType mtype) noexcept
+void GCodeQueue::Diagnostics(MessageType mtype, unsigned int queueNumber) noexcept
{
if (queuedItems == nullptr)
{
- reprap.GetPlatform().Message(mtype, "Code queue is empty\n");
+ reprap.GetPlatform().MessageF(mtype, "Code queue %u is empty\n", queueNumber);
}
else
{
@@ -249,7 +229,7 @@ void GCodeQueue::Diagnostics(MessageType mtype) noexcept
if (!reprap.UsingSbcInterface())
#endif
{
- reprap.GetPlatform().MessageF(mtype, "Queued '%.*s' for move %" PRIu32 "\n", item->dataLength, item->data, item->executeAtMove);
+ reprap.GetPlatform().MessageF(mtype, "Queue %u has '%.*s' for move %" PRIu32 "\n", queueNumber, item->dataLength, item->data, item->executeAtMove);
}
} while ((item = item->Next()) != nullptr);
}
@@ -257,7 +237,7 @@ void GCodeQueue::Diagnostics(MessageType mtype) noexcept
// QueuedCode class
-void QueuedCode::AssignFrom(GCodeBuffer &gb) noexcept
+void QueuedCode::AssignFrom(const GCodeBuffer &gb) noexcept
{
#if HAS_SBC_INTERFACE
isBinary = gb.IsBinary();
diff --git a/src/GCodes/GCodeQueue.h b/src/GCodes/GCodeQueue.h
index 1eb6c647..3f751927 100644
--- a/src/GCodes/GCodeQueue.h
+++ b/src/GCodes/GCodeQueue.h
@@ -23,14 +23,15 @@ public:
bool FillBuffer(GCodeBuffer *gb) noexcept override; // If there is another move to execute at this time, fill a buffer
size_t BytesCached() const noexcept override; // How many bytes have been cached?
- bool QueueCode(GCodeBuffer &gb, uint32_t scheduleAt) noexcept; // Queue a G-code
+ bool QueueCode(const GCodeBuffer &gb) noexcept; // Queue a G-code
void PurgeEntries() noexcept; // Remove stored codes when a print is being paused
void Clear() noexcept; // Clean up all the stored codes
bool IsIdle() const noexcept; // Return true if there is nothing to do
- void Diagnostics(MessageType mtype) noexcept;
+ void Diagnostics(MessageType mtype, unsigned int queueNumber) noexcept;
- static bool ShouldQueueCode(GCodeBuffer &gb) THROWS(GCodeException); // Return true if this code should be queued
+ static bool ShouldQueueG10(GCodeBuffer &gb) noexcept; // Return true if this code should be queued
+ static bool ShouldQueueMCode(GCodeBuffer &gb) THROWS(GCodeException); // Return true if this code should be queued
private:
QueuedCode *freeItems;
@@ -58,7 +59,7 @@ private:
uint32_t executeAtMove;
- void AssignFrom(GCodeBuffer &gb) noexcept;
+ void AssignFrom(const GCodeBuffer &gb) noexcept;
void AssignTo(GCodeBuffer *gb) noexcept;
};
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index fc3541db..18fd99e5 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -26,6 +26,7 @@
#include "GCodes.h"
#include "GCodeBuffer/GCodeBuffer.h"
+#include "GCodeBuffer/ExpressionParser.h"
#include "GCodeQueue.h"
#include <Heating/Heat.h>
#include <Platform/Platform.h>
@@ -65,11 +66,12 @@ GCodes::GCodes(Platform& p) noexcept :
#if HAS_AUX_DEVICES && ALLOW_ARBITRARY_PANELDUE_PORT
serialChannelForPanelDueFlashing(1),
#endif
- platform(p), machineType(MachineType::fff), active(false)
+ platform(p),
+ machineType(MachineType::fff), active(false),
#if HAS_VOLTAGE_MONITOR
- , powerFailScript(nullptr)
+ powerFailScript(nullptr),
#endif
- , isFlashing(false),
+ isFlashing(false),
#if SUPPORT_PANELDUE_FLASH
isFlashingPanelDue(false),
#endif
@@ -84,20 +86,33 @@ GCodes::GCodes(Platform& p) noexcept :
#else
FileGCodeInput * const fileInput = nullptr;
#endif
- fileGCode = new GCodeBuffer(GCodeChannel::File, nullptr, fileInput, GenericMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::File)] = new GCodeBuffer(GCodeChannel::File, nullptr, fileInput, GenericMessage);
+ moveStates[0].codeQueue = new GCodeQueue();
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue)] = new GCodeBuffer(GCodeChannel::Queue, moveStates[0].codeQueue, fileInput, GenericMessage);
-# if SUPPORT_HTTP || HAS_SBC_INTERFACE
- httpInput = new NetworkGCodeInput();
- httpGCode = new GCodeBuffer(GCodeChannel::HTTP, httpInput, fileInput, HttpMessage);
+#if SUPPORT_ASYNC_MOVES
+# if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
+ FileGCodeInput * const file2Input = new FileGCodeInput(); // use a separate FileInput object so that both file input streams can run concurrently
# else
- httpGCode = nullptr;
-# endif // SUPPORT_HTTP || HAS_SBC_INTERFACE
-# if SUPPORT_TELNET || HAS_SBC_INTERFACE
+ FileGCodeInput * const file2Input = nullptr;
+# endif
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::File2)] = new GCodeBuffer(GCodeChannel::File2, nullptr, file2Input, GenericMessage);
+ moveStates[1].codeQueue = new GCodeQueue();
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue2)] = new GCodeBuffer(GCodeChannel::Queue2, moveStates[1].codeQueue, fileInput, GenericMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue2)]->SetActiveQueueNumber(1); // so that all commands read from this queue get executed on queue #1 instead of the default #0
+#endif
+#if SUPPORT_HTTP || HAS_SBC_INTERFACE
+ httpInput = new NetworkGCodeInput();
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::HTTP)] = new GCodeBuffer(GCodeChannel::HTTP, httpInput, fileInput, HttpMessage);
+#else
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::HTTP)] = nullptr;
+#endif // SUPPORT_HTTP || HAS_SBC_INTERFACE
+#if SUPPORT_TELNET || HAS_SBC_INTERFACE
telnetInput = new NetworkGCodeInput();
- telnetGCode = new GCodeBuffer(GCodeChannel::Telnet, telnetInput, fileInput, TelnetMessage, Compatibility::Marlin);
-# else
- telnetGCode = nullptr;
-# endif // SUPPORT_TELNET || HAS_SBC_INTERFACE
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Telnet)] = new GCodeBuffer(GCodeChannel::Telnet, telnetInput, fileInput, TelnetMessage, Compatibility::Marlin);
+#else
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Telnet)] = nullptr;
+#endif // SUPPORT_TELNET || HAS_SBC_INTERFACE
#if defined(SERIAL_MAIN_DEVICE)
# if SAME5x
// SAME5x USB driver already uses an efficient buffer for receiving data from USB
@@ -106,48 +121,45 @@ GCodes::GCodes(Platform& p) noexcept :
// Old USB driver is inefficient when read in single-character mode
BufferedStreamGCodeInput * const usbInput = new BufferedStreamGCodeInput(SERIAL_MAIN_DEVICE);
# endif
- usbGCode = new GCodeBuffer(GCodeChannel::USB, usbInput, fileInput, UsbMessage, Compatibility::Marlin);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USB)] = new GCodeBuffer(GCodeChannel::USB, usbInput, fileInput, UsbMessage, Compatibility::Marlin);
#elif HAS_SBC_INTERFACE
- usbGCode = new GCodeBuffer(GCodeChannel::USB, nullptr, fileInput, UsbMessage, Compatbility::marlin);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USB)] = new GCodeBuffer(GCodeChannel::USB, nullptr, fileInput, UsbMessage, Compatbility::marlin);
#else
- usbGCode = nullptr;
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USB)] = nullptr;
#endif
#if HAS_AUX_DEVICES
StreamGCodeInput * const auxInput = new StreamGCodeInput(SERIAL_AUX_DEVICE);
- auxGCode = new GCodeBuffer(GCodeChannel::Aux, auxInput, fileInput, AuxMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux)] = new GCodeBuffer(GCodeChannel::Aux, auxInput, fileInput, AuxMessage);
#elif HAS_SBC_INTERFACE
- auxGCode = new GCodeBuffer(GCodeChannel::Aux, nullptr, fileInput, AuxMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux)] = new GCodeBuffer(GCodeChannel::Aux, nullptr, fileInput, AuxMessage);
#else
- auxGCode = nullptr;
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux)] = nullptr;
#endif
- triggerGCode = new GCodeBuffer(GCodeChannel::Trigger, nullptr, fileInput, GenericMessage);
-
- codeQueue = new GCodeQueue();
- queuedGCode = new GCodeBuffer(GCodeChannel::Queue, codeQueue, fileInput, GenericMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Trigger)] = new GCodeBuffer(GCodeChannel::Trigger, nullptr, fileInput, GenericMessage);
-#if SUPPORT_12864_LCD || HAS_SBC_INTERFACE
- lcdGCode = new GCodeBuffer(GCodeChannel::LCD, nullptr, fileInput, LcdMessage);
+#if SUPPORT_DIRECT_LCD || HAS_SBC_INTERFACE
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::LCD)] = new GCodeBuffer(GCodeChannel::LCD, nullptr, fileInput, LcdMessage);
#else
- lcdGCode = nullptr;
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::LCD)] = nullptr;
#endif
#if HAS_SBC_INTERFACE
- spiGCode = new GCodeBuffer(GCodeChannel::SBC, nullptr, fileInput, GenericMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::SBC)] = new GCodeBuffer(GCodeChannel::SBC, nullptr, fileInput, GenericMessage);
#else
- spiGCode = nullptr;
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::SBC)] = nullptr;
#endif
- daemonGCode = new GCodeBuffer(GCodeChannel::Daemon, nullptr, fileInput, GenericMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Daemon)] = new GCodeBuffer(GCodeChannel::Daemon, nullptr, fileInput, GenericMessage);
#if defined(SERIAL_AUX2_DEVICE)
StreamGCodeInput * const aux2Input = new StreamGCodeInput(SERIAL_AUX2_DEVICE);
- aux2GCode = new GCodeBuffer(GCodeChannel::Aux2, aux2Input, fileInput, Aux2Message);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux2)] = new GCodeBuffer(GCodeChannel::Aux2, aux2Input, fileInput, Aux2Message);
#elif HAS_SBC_INTERFACE
- aux2GCode = new GCodeBuffer(GCodeChannel::Aux2, nullptr, fileInput, Aux2Message);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux2)] = new GCodeBuffer(GCodeChannel::Aux2, nullptr, fileInput, Aux2Message);
#else
- aux2GCode = nullptr;
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux2)] = nullptr;
#endif
- autoPauseGCode = new GCodeBuffer(GCodeChannel::Autopause, nullptr, fileInput, GenericMessage);
+ gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Autopause)] = new GCodeBuffer(GCodeChannel::Autopause, nullptr, fileInput, GenericMessage);
}
void GCodes::Exit() noexcept
@@ -167,7 +179,7 @@ void GCodes::Init() noexcept
Reset();
- virtualExtruderPosition = rawExtruderTotal = 0.0;
+ rawExtruderTotal = 0.0;
for (float& f : rawExtruderTotalByDrive)
{
f = 0.0;
@@ -180,15 +192,13 @@ void GCodes::Init() noexcept
limitAxes = noMovesBeforeHoming = true;
SetAllAxesNotHomed();
- lastDefaultFanSpeed = 0.0;
-
lastAuxStatusReportType = -1; // no status reports requested yet
laserMaxPower = DefaultMaxLaserPower;
laserPowerSticky = false;
#if SUPPORT_SCANNER
- reprap.GetScanner().SetGCodeBuffer(usbGCode);
+ reprap.GetScanner().SetGCodeBuffer(UsbGCode());
#endif
#if SUPPORT_LED_STRIPS
@@ -213,9 +223,9 @@ void GCodes::Reset() noexcept
}
}
- if (auxGCode != nullptr)
+ if (AuxGCode() != nullptr)
{
- auxGCode->SetCommsProperties(1); // by default, we require a checksum on the aux port
+ AuxGCode()->SetCommsProperties(1); // by default, we require a checksum on the aux port
}
nextGcodeSource = 0;
@@ -223,11 +233,12 @@ void GCodes::Reset() noexcept
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
fileToPrint.Close();
#endif
- speedFactor = 1.0;
for (size_t i = 0; i < MaxExtruders; ++i)
{
- extrusionFactors[i] = volumetricExtrusionFactors[i] = 1.0;
+ extrusionFactors[i] = 1.0;
+ filamentDiameters[i] = DefaultFilamentDiameter;
+ volumetricExtrusionFactors[i] = 4.0/(fsquare(DefaultFilamentDiameter) * Pi);
}
for (size_t i = 0; i < MaxAxes; ++i)
@@ -243,37 +254,18 @@ void GCodes::Reset() noexcept
g68Angle = g68Centre[0] = g68Centre[1] = 0.0; // no coordinate rotation
#endif
- moveState.currentCoordinateSystem = 0;
-
- for (float& f : moveState.coords)
+ for (MovementState& ms : moveStates)
{
- f = 0.0; // clear out all axis and extruder coordinates
+ ms.Reset();
+ reprap.GetMove().GetKinematics().GetAssumedInitialPosition(numVisibleAxes, ms.coords);
+ ToolOffsetInverseTransform(ms);
}
- ClearMove();
-
for (float& f : currentBabyStepOffsets)
{
f = 0.0; // clear babystepping before calling ToolOffsetInverseTransform
}
- moveState.currentZHop = 0.0; // clear this before calling ToolOffsetInverseTransform
- newToolNumber = -1;
-
- moveState.tool = nullptr;
- moveState.virtualExtruderPosition = 0.0;
-#if SUPPORT_LASER || SUPPORT_IOBITS
- moveState.laserPwmOrIoBits.Clear();
-#endif
- reprap.GetMove().GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveState.coords);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
- updateUserPositionGb = nullptr;
-
- for (RestorePoint& rp : numberedRestorePoints)
- {
- rp.Init();
- }
-
for (TriggerItem& tr : triggers)
{
tr.Init();
@@ -286,19 +278,15 @@ void GCodes::Reset() noexcept
lastDuration = 0;
pauseState = PauseState::notPaused;
- pausedInMacro = false;
#if HAS_VOLTAGE_MONITOR
isPowerFailPaused = false;
#endif
doingToolChange = false;
doingManualBedProbe = false;
-#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
- fileOffsetToPrint = 0;
- restartMoveFractionDone = 0.0;
+#if SUPPORT_PROBE_POINTS_FILE
+ probePointsFileLoaded = false;
#endif
- printFilePositionAtMacroStart = 0;
deferredPauseCommandPending = nullptr;
- moveState.filePos = noFilePosition;
firmwareUpdateModuleMap.Clear();
isFlashing = false;
#if SUPPORT_PANELDUE_FLASH
@@ -308,7 +296,6 @@ void GCodes::Reset() noexcept
buildObjects.Init();
- codeQueue->Clear();
cancelWait = isWaiting = displayNoToolWarning = false;
for (const GCodeBuffer*& gbp : resourceOwners)
@@ -322,7 +309,7 @@ bool GCodes::DoingFileMacro() const noexcept
{
for (const GCodeBuffer *gbp : gcodeSources)
{
- if (gbp != nullptr && gbp != daemonGCode && gbp->IsDoingFileMacro())
+ if (gbp != nullptr && gbp != DaemonGCode() && gbp->IsDoingFileMacro())
{
return true;
}
@@ -343,44 +330,26 @@ bool GCodes::WaitingForAcknowledgement() const noexcept
return false;
}
-// Return the current position of the file being printed in bytes.
-// May return noFilePosition if allowNoFilePos is true
-FilePosition GCodes::GetFilePosition(bool allowNoFilePos) const noexcept
+FilePosition GCodes::GetPrintingFilePosition() const noexcept
{
-#if HAS_SBC_INTERFACE
- if (!reprap.UsingSbcInterface())
-#endif
- {
-#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
- const FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
- if (!fileBeingPrinted.IsLive())
- {
- return allowNoFilePos ? noFilePosition : 0;
- }
-#endif
- }
-
-#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
- const FilePosition pos = (fileGCode->IsDoingFileMacro())
- ? printFilePositionAtMacroStart // the position before we started executing the macro
- : fileGCode->GetFilePosition(); // the actual position, allowing for bytes cached but not yet processed
- return (pos != noFilePosition || allowNoFilePos) ? pos : 0;
+#if HAS_SBC_INTERFACE || HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
+ return FileGCode()->GetPrintingFilePosition(false);
#else
- return allowNoFilePos ? noFilePosition : 0;
+ return 0;
#endif
}
// Start running the config file
bool GCodes::RunConfigFile(const char* fileName) noexcept
{
- runningConfigFile = DoFileMacro(*triggerGCode, fileName, false, AsyncSystemMacroCode);
+ runningConfigFile = DoFileMacro(*TriggerGCode(), fileName, false, AsyncSystemMacroCode);
return runningConfigFile;
}
// Return true if the trigger G-code buffer is busy running config.g or a trigger file
bool GCodes::IsTriggerBusy() const noexcept
{
- return triggerGCode->IsDoingFile();
+ return TriggerGCode()->IsDoingFile();
}
// Copy the feed rate etc. from the channel that was running config.g to the input channels
@@ -429,16 +398,19 @@ void GCodes::Spin() noexcept
}
#endif
- if (updateUserPositionGb != nullptr)
+ for (MovementState& ms : moveStates)
{
- UpdateCurrentUserPosition(*updateUserPositionGb);
- updateUserPositionGb = nullptr;
+ if (ms.updateUserPositionGb != nullptr)
+ {
+ UpdateCurrentUserPosition(*ms.updateUserPositionGb);
+ ms.updateUserPositionGb = nullptr;
+ }
}
CheckTriggers();
// The autoPause buffer has priority, so spin that one first. It may have to wait for other buffers to release locks etc.
- (void)SpinGCodeBuffer(*autoPauseGCode);
+ (void)SpinGCodeBuffer(*AutoPauseGCode());
// Use round-robin scheduling for the other input sources
// Scan the GCode input channels until we find one that we can do some useful work with, or we have scanned them all.
@@ -448,11 +420,15 @@ void GCodes::Spin() noexcept
{
GCodeBuffer * const gbp = gcodeSources[nextGcodeSource];
++nextGcodeSource; // move on to the next gcode source ready for next time
- if (nextGcodeSource == ARRAY_SIZE(gcodeSources) - 1) // the last one is autoPauseGCode, so don't do it again
+ if (nextGcodeSource == GCodeChannel::Autopause)
+ {
+ ++nextGcodeSource; // don't do autoPause again
+ }
+ if (nextGcodeSource == ARRAY_SIZE(gcodeSources))
{
nextGcodeSource = 0;
}
- if (gbp != nullptr && (gbp != auxGCode || !IsFlashingPanelDue())) // skip auxGCode while flashing PanelDue is in progress
+ if (gbp != nullptr && (gbp != AuxGCode() || !IsFlashingPanelDue())) // skip auxGCode while flashing PanelDue is in progress
{
if (SpinGCodeBuffer(*gbp)) // if we did something useful
{
@@ -543,7 +519,7 @@ bool GCodes::StartNextGCode(GCodeBuffer& gb, const StringRef& reply) noexcept
// - if the pause state is paused or resuming, don't execute
// - if the state is pausing then don't execute, unless we are executing a macro (because it could be the pause macro or filament change macro)
// - if there is a deferred pause pending, don't execute once we have finished the current macro
- if (&gb == fileGCode
+ if ( gb.IsFileChannel()
&& ( pauseState > PauseState::pausing // paused or resuming
|| (!gb.IsDoingFileMacro() && (deferredPauseCommandPending != nullptr || pauseState == PauseState::pausing))
)
@@ -562,14 +538,14 @@ bool GCodes::StartNextGCode(GCodeBuffer& gb, const StringRef& reply) noexcept
{
return DoFilePrint(gb, reply);
}
- else if (&gb == autoPauseGCode && !gb.LatestMachineState().waitingForAcknowledgement)
+ else if (&gb == AutoPauseGCode() && !gb.LatestMachineState().waitingForAcknowledgement)
{
if (Event::StartProcessing())
{
ProcessEvent(gb); // call out to separate function to avoid increasing stack usage of this function
}
}
- else if (&gb == daemonGCode
+ else if (&gb == DaemonGCode()
#if SUPPORT_REMOTE_COMMANDS
&& !CanInterface::InExpansionMode() // looking for the daemon.g file increases the loop time too much
#endif
@@ -586,7 +562,7 @@ bool GCodes::StartNextGCode(GCodeBuffer& gb, const StringRef& reply) noexcept
}
else
#if SUPPORT_SCANNER
- if (!(&gb == usbGCode && reprap.GetScanner().IsRegistered()))
+ if (!(&gb == UsbGCode() && reprap.GetScanner().IsRegistered()))
#endif
{
const bool gotCommand = (gb.GetNormalInput() != nullptr) && gb.GetNormalInput()->FillBuffer(&gb);
@@ -630,16 +606,25 @@ bool GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept
{
if (gb.IsFileFinished())
{
+ if (!LockMovementAndWaitForStandstill(gb)) // wait until movement has finished and deferred command queue has caught up
+ {
+ return false;
+ }
+
if (gb.LatestMachineState().GetPrevious() == nullptr)
{
// Finished printing SD card file.
// We never get here if the file ends in M0 because CancelPrint gets called directly in that case.
// Don't close the file until all moves have been completed, in case the print gets paused.
// Also, this keeps the state as 'Printing' until the print really has finished.
- if (LockMovementAndWaitForStandstill(gb)) // wait until movement has finished and deferred command queue has caught up
+ if (&gb == FileGCode())
{
StopPrint(StopPrintReason::normalCompletion);
}
+ else
+ {
+ UnlockAll(gb);
+ }
return true;
}
@@ -729,7 +714,7 @@ bool GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeInputReadResult::noData:
// We have reached the end of the file. Check for the last line of gcode not ending in newline.
- if (gb.FileEnded()) // append a newline if necessary and deal with any pending file write
+ if (gb.FileEnded()) // append a newline if necessary and deal with any pending file write
{
bool done;
try
@@ -760,7 +745,12 @@ bool GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept
return true;
}
- gb.Init(); // mark buffer as empty
+ gb.Init(); // mark buffer as empty
+
+ if (!LockMovementAndWaitForStandstill(gb)) // wait until movement has finished and deferred command queue has caught up
+ {
+ return false;
+ }
if (gb.LatestMachineState().GetPrevious() == nullptr)
{
@@ -768,10 +758,23 @@ bool GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept
// We never get here if the file ends in M0 because CancelPrint gets called directly in that case.
// Don't close the file until all moves have been completed, in case the print gets paused.
// Also, this keeps the state as 'Printing' until the print really has finished.
- if (LockMovementAndWaitForStandstill(gb)) // wait until movement has finished and deferred command queue has caught up
+ if (&gb == FileGCode())
{
+ if (reprap.Debug(moduleGcodes))
+ {
+ debugPrintf("File stopping print\n");
+ }
StopPrint(StopPrintReason::normalCompletion);
}
+ else
+ {
+ if (reprap.Debug(moduleGcodes))
+ {
+ debugPrintf("Other stopping print\n");
+ }
+ UnlockAll(gb);
+ fd.Close();
+ }
}
else
{
@@ -796,13 +799,15 @@ bool GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept
}
// Restore positions etc. when exiting simulation mode
-void GCodes::EndSimulation(GCodeBuffer *gb) noexcept
+void GCodes::EndSimulation(GCodeBuffer *null gb) noexcept
{
// Ending a simulation, so restore the position
- RestorePosition(simulationRestorePoint, gb);
- reprap.SelectTool(simulationRestorePoint.toolNumber, true);
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
- reprap.GetMove().SetNewPosition(moveState.coords, true);
+ const unsigned int queueNumber = (gb == nullptr) ? 0 : gb->GetActiveQueueNumber(); //TODO handle null gb properly
+ MovementState& ms = moveStates[queueNumber];
+ RestorePosition(ms.simulationRestorePoint, gb);
+ ms.SelectTool(ms.simulationRestorePoint.toolNumber, true);
+ ToolOffsetTransform(ms);
+ reprap.GetMove().SetNewPosition(ms.coords, true, queueNumber);
axesVirtuallyHomed = axesHomed;
reprap.MoveUpdated();
}
@@ -824,30 +829,31 @@ void GCodes::CheckTriggers() noexcept
const unsigned int lowestTriggerPending = triggersPending.LowestSetBit();
if (lowestTriggerPending == 0)
{
- triggersPending.ClearBit(lowestTriggerPending); // clear the trigger
+ triggersPending.ClearBit(lowestTriggerPending); // clear the trigger
DoEmergencyStop();
}
- else if (!IsTriggerBusy() && triggerGCode->GetState() == GCodeState::normal) // if we are not already executing a trigger or config.g
+ else if (!IsTriggerBusy() && TriggerGCode()->GetState() == GCodeState::normal) // if we are not already executing a trigger or config.g
{
if (lowestTriggerPending == 1)
{
if (!IsReallyPrinting())
{
- triggersPending.ClearBit(lowestTriggerPending); // ignore a pause trigger if we are already paused or not printing
+ triggersPending.ClearBit(lowestTriggerPending); // ignore a pause trigger if we are already paused or not printing
}
- else if (LockMovement(*triggerGCode)) // need to lock movement before executing the pause macro
+ else if (DoAsynchronousPause(*TriggerGCode(), PrintPausedReason::trigger, GCodeState::pausing1))
{
- triggersPending.ClearBit(lowestTriggerPending); // clear the trigger
- DoPause(*triggerGCode, PrintPausedReason::trigger, GCodeState::pausing1);
- platform.SendAlert(GenericMessage, "Print paused by external trigger", "Printing paused", 1, 0.0, AxesBitmap());
+ if (reprap.SendSimpleAlert(GenericMessage, "Print paused by external trigger", "Printing paused"))
+ {
+ triggersPending.ClearBit(lowestTriggerPending); // clear the trigger
+ }
}
}
else
{
- triggersPending.ClearBit(lowestTriggerPending); // clear the trigger
+ triggersPending.ClearBit(lowestTriggerPending); // clear the trigger
String<StringLength20> filename;
filename.printf("trigger%u.g", lowestTriggerPending);
- DoFileMacro(*triggerGCode, filename.c_str(), true, AsyncSystemMacroCode);
+ DoFileMacro(*TriggerGCode(), filename.c_str(), true, AsyncSystemMacroCode);
}
}
}
@@ -861,71 +867,126 @@ void GCodes::DoEmergencyStop() noexcept
platform.Message(GenericMessage, "Emergency Stop! Reset the controller to continue.\n");
}
-// Pause the print
-// Before calling this, check that we are doing a file print that isn't already paused and get the movement lock.
-void GCodes::DoPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newState) noexcept
+// Pause the print because of a command in the print file itself
+// Return true if successful, false if we can't yet
+bool GCodes::DoSynchronousPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newState) noexcept
{
- pausedInMacro = false;
- if (&gb == fileGCode)
+ // Pausing because of a command in the file itself
+ if (!LockMovementAndWaitForStandstill(gb))
{
- // Pausing a file print because of a command in the file itself
- SavePosition(pauseRestorePoint, gb);
+ return false;
}
- else
+
+ MovementState& ms = GetMovementState(gb);
+ ms.pausedInMacro = false;
+ ms.SavePosition(PauseRestorePointNumber, numVisibleAxes, gb.LatestMachineState().feedRate, gb.GetJobFilePosition()); //TODO not correct, need to use correct gb for each movement system!
+
+#if SUPPORT_LASER
+ if (machineType == MachineType::laser)
+ {
+ ms.laserPwmOrIoBits.laserPwm = 0; // turn off the laser when we start moving
+ }
+#endif
+
+ ms.pauseRestorePoint.toolNumber = ms.GetCurrentToolNumber();
+ ms.pauseRestorePoint.fanSpeed = ms.virtualFanSpeed;
+
+#if HAS_SBC_INTERFACE
+ if (reprap.UsingSbcInterface())
+ {
+ // Prepare notification for the SBC
+ reprap.GetSbcInterface().SetPauseReason(ms.pauseRestorePoint.filePos, reason);
+ }
+#endif
+
+ if (ms.pauseRestorePoint.filePos == noFilePosition)
+ {
+ // Make sure we expose usable values (which noFilePosition is not)
+ ms.pauseRestorePoint.filePos = 0;
+ }
+
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
+ if (!IsSimulating())
+ {
+ //TODO need to sync here!
+ SaveResumeInfo(false); // create the resume file so that we can resume after power down
+ }
+#endif
+
+ gb.SetState(newState);
+ pauseState = PauseState::pausing;
+
+ reprap.StateUpdated(); // test DWC/DSF that we have changed a restore point
+ return true;
+}
+
+// Pause the print because of an event or command not in the file being printed
+// Return true if successful, false if we can't yet
+// Before calling this, check that we are doing a file print that isn't already paused
+// 'gb' is the GCode buffer that commanded the pause
+bool GCodes::DoAsynchronousPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newState) noexcept
+{
+ if (!LockAllMovement(gb))
+ {
+ return false;
+ }
+
+ for (size_t moveSystemNumber = 0; moveSystemNumber < ARRAY_SIZE(moveStates); ++moveSystemNumber)
{
- // Pausing a file print via another input source or for some other reason
- pauseRestorePoint.feedRate = fileGCode->LatestMachineState().feedRate; // set up the default
+ MovementState& ms = moveStates[moveSystemNumber];
+ ms.pausedInMacro = false;
+ ms.pauseRestorePoint.feedRate = ms.feedRate;
- const bool movesSkipped = reprap.GetMove().PausePrint(pauseRestorePoint); // tell Move we wish to pause the current print
+ const bool movesSkipped = reprap.GetMove().PausePrint(moveSystemNumber, ms.pauseRestorePoint); // tell Move we wish to pause this queue
if (movesSkipped)
{
// The PausePrint call has filled in the restore point with machine coordinates
- ToolOffsetInverseTransform(pauseRestorePoint.moveCoords, moveState.currentUserPosition); // transform the returned coordinates to user coordinates
- ClearMove();
+ ToolOffsetInverseTransform(ms, ms.pauseRestorePoint.moveCoords, ms.currentUserPosition); // transform the returned coordinates to user coordinates
+ ms.ClearMove();
}
- else if (moveState.segmentsLeft != 0)
+ else if (ms.segmentsLeft != 0)
{
// We were not able to skip any moves, however we can skip the move that is waiting
- pauseRestorePoint.virtualExtruderPosition = moveState.virtualExtruderPosition;
- pauseRestorePoint.filePos = moveState.filePos;
- pauseRestorePoint.feedRate = moveState.feedRate;
- pauseRestorePoint.proportionDone = moveState.GetProportionDone();
- pauseRestorePoint.initialUserC0 = moveState.initialUserC0;
- pauseRestorePoint.initialUserC1 = moveState.initialUserC1;
- ToolOffsetInverseTransform(pauseRestorePoint.moveCoords, moveState.currentUserPosition); // transform the returned coordinates to user coordinates
- ClearMove();
+ ms.pauseRestorePoint.virtualExtruderPosition = ms.moveStartVirtualExtruderPosition;
+ ms.pauseRestorePoint.filePos = ms.filePos;
+ ms.pauseRestorePoint.feedRate = ms.feedRate;
+ ms.pauseRestorePoint.proportionDone = ms.GetProportionDone();
+ ms.pauseRestorePoint.initialUserC0 = ms.initialUserC0;
+ ms.pauseRestorePoint.initialUserC1 = ms.initialUserC1;
+ ToolOffsetInverseTransform(ms, ms.pauseRestorePoint.moveCoords, ms.currentUserPosition); // transform the returned coordinates to user coordinates
+ ms.ClearMove();
}
else
{
// We were not able to skip any moves, and there is no move waiting
- pauseRestorePoint.feedRate = fileGCode->LatestMachineState().feedRate;
- pauseRestorePoint.virtualExtruderPosition = virtualExtruderPosition;
- pauseRestorePoint.proportionDone = 0.0;
+ ms.pauseRestorePoint.feedRate = FileGCode()->LatestMachineState().feedRate;
+ ms.pauseRestorePoint.virtualExtruderPosition = ms.latestVirtualExtruderPosition;
+ ms.pauseRestorePoint.proportionDone = 0.0;
// TODO: when using RTOS there is a possible race condition in the following,
// because we might try to pause when a waiting move has just been added but before the gcode buffer has been re-initialised ready for the next command
- pauseRestorePoint.filePos = GetFilePosition(true);
- while (fileGCode->IsDoingFileMacro()) // must call this after GetFilePosition because this changes IsDoingFileMacro
+ ms.pauseRestorePoint.filePos = FileGCode()->GetPrintingFilePosition(true);
+ while (FileGCode()->IsDoingFileMacro()) // must call this after GetFilePosition because this changes IsDoingFileMacro
{
- pausedInMacro = true;
- fileGCode->PopState(false);
+ ms.pausedInMacro = true;
+ FileGCode()->PopState(false);
}
#if SUPPORT_LASER || SUPPORT_IOBITS
- pauseRestorePoint.laserPwmOrIoBits = moveState.laserPwmOrIoBits;
+ ms.pauseRestorePoint.laserPwmOrIoBits = ms.laserPwmOrIoBits;
#endif
}
// Replace the paused machine coordinates by user coordinates, which we updated earlier if they were returned by Move::PausePrint
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- pauseRestorePoint.moveCoords[axis] = moveState.currentUserPosition[axis];
+ ms.pauseRestorePoint.moveCoords[axis] = ms.currentUserPosition[axis];
}
#if HAS_SBC_INTERFACE
if (reprap.UsingSbcInterface())
{
- fileGCode->Init(); // clear the next move
- UnlockAll(*fileGCode); // release any locks it had
+ FileGCode()->Init(); // clear the next move
+ UnlockAll(*FileGCode()); // release any locks it had
}
else
#endif
@@ -933,39 +994,54 @@ void GCodes::DoPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newSt
#if HAS_MASS_STORAGE
// If we skipped any moves, reset the file pointer to the start of the first move we need to replay
// The following could be delayed until we resume the print
- if (pauseRestorePoint.filePos != noFilePosition)
+ if (ms.pauseRestorePoint.filePos != noFilePosition)
{
- FileData& fdata = fileGCode->LatestMachineState().fileState;
+ FileData& fdata = FileGCode()->LatestMachineState().fileState;
if (fdata.IsLive())
{
- fileGCode->RestartFrom(pauseRestorePoint.filePos); // TODO we ought to restore the line number too, but currently we don't save it
- UnlockAll(*fileGCode); // release any locks it had
+ FileGCode()->RestartFrom(ms.pauseRestorePoint.filePos); // TODO we ought to restore the line number too, but currently we don't save it
+ UnlockAll(*FileGCode()); // release any locks it had
}
}
#endif
}
- codeQueue->PurgeEntries();
+ ms.codeQueue->PurgeEntries();
if (reprap.Debug(moduleGcodes))
{
- platform.MessageF(GenericMessage, "Paused print, file offset=%" PRIu32 "\n", pauseRestorePoint.filePos);
+ platform.MessageF(GenericMessage, "Paused print, file offset=%" PRIu32 "\n", ms.pauseRestorePoint.filePos);
}
- }
#if SUPPORT_LASER
- if (machineType == MachineType::laser)
- {
- moveState.laserPwmOrIoBits.laserPwm = 0; // turn off the laser when we start moving
- }
+ if (machineType == MachineType::laser)
+ {
+ ms.laserPwmOrIoBits.laserPwm = 0; // turn off the laser when we start moving
+ }
+#endif
+
+ ms.pauseRestorePoint.toolNumber = ms.GetCurrentToolNumber();
+ ms.pauseRestorePoint.fanSpeed = ms.virtualFanSpeed;
+
+#if HAS_SBC_INTERFACE
+ if (reprap.UsingSbcInterface())
+ {
+ // Prepare notification for the SBC
+ reprap.GetSbcInterface().SetPauseReason(ms.pauseRestorePoint.filePos, reason);
+ }
#endif
- pauseRestorePoint.toolNumber = reprap.GetCurrentToolNumber();
- pauseRestorePoint.fanSpeed = lastDefaultFanSpeed;
+ if (ms.pauseRestorePoint.filePos == noFilePosition)
+ {
+ // Make sure we expose usable values (which noFilePosition is not)
+ ms.pauseRestorePoint.filePos = 0;
+ }
+ }
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
if (!IsSimulating())
{
+ //TODO need to sync here!
SaveResumeInfo(false); // create the resume file so that we can resume after power down
}
#endif
@@ -973,27 +1049,14 @@ void GCodes::DoPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newSt
gb.SetState(newState);
pauseState = PauseState::pausing;
-#if HAS_SBC_INTERFACE
- if (reprap.UsingSbcInterface())
- {
- // Prepare notification for the SBC
- reprap.GetSbcInterface().SetPauseReason(pauseRestorePoint.filePos, reason);
- }
-#endif
-
- if (pauseRestorePoint.filePos == noFilePosition)
- {
- // Make sure we expose usable values (which noFilePosition is not)
- pauseRestorePoint.filePos = 0;
- }
-
reprap.StateUpdated(); // test DWC/DSF that we have changed a restore point
+ return true;
}
// Check if a pause is pending, action it if so
void GCodes::CheckForDeferredPause(GCodeBuffer& gb) noexcept
{
- if (&gb == fileGCode && !gb.IsDoingFileMacro() && deferredPauseCommandPending != nullptr)
+ if (gb.IsFileChannel() && !gb.IsDoingFileMacro() && deferredPauseCommandPending != nullptr)
{
gb.PutAndDecode(deferredPauseCommandPending);
deferredPauseCommandPending = nullptr;
@@ -1024,9 +1087,9 @@ bool GCodes::IsReallyPrintingOrResuming() const noexcept
bool GCodes::IsHeatingUp() const noexcept
{
int num;
- return fileGCode->IsExecuting()
- && fileGCode->GetCommandLetter() == 'M'
- && ((num = fileGCode->GetCommandNumber()) == 109 || num == 116 || num == 190 || num == 191);
+ return FileGCode()->IsExecuting()
+ && FileGCode()->GetCommandLetter() == 'M'
+ && ((num = FileGCode()->GetCommandNumber()) == 109 || num == 116 || num == 190 || num == 191);
}
#if HAS_VOLTAGE_MONITOR || HAS_STALL_DETECT
@@ -1034,77 +1097,83 @@ bool GCodes::IsHeatingUp() const noexcept
// Do an emergency pause following loss of power or a motor stall returning true if successful, false if needs to be retried
bool GCodes::DoEmergencyPause() noexcept
{
- if (!autoPauseGCode->IsCompletelyIdle())
+ if (!AutoPauseGCode()->IsCompletelyIdle())
{
return false; // we can't pause if the auto pause thread is busy already
}
// Save the resume info, stop movement immediately and run the low voltage pause script to lift the nozzle etc.
- GrabMovement(*autoPauseGCode);
+ GrabMovement(*AutoPauseGCode());
- // When we use RTOS there is a possible race condition in the following, because we might try to pause when a waiting move has just been added
- // but before the gcode buffer has been re-initialised ready for the next command. So start a critical section.
- TaskCriticalSectionLocker lock;
-
- const bool movesSkipped = reprap.GetMove().LowPowerOrStallPause(pauseRestorePoint);
- if (movesSkipped)
- {
- // The PausePrint call has filled in the restore point with machine coordinates
- ToolOffsetInverseTransform(pauseRestorePoint.moveCoords, moveState.currentUserPosition); // transform the returned coordinates to user coordinates
- ClearMove();
- }
- else if (moveState.segmentsLeft != 0 && moveState.filePos != noFilePosition)
+ for (size_t moveSystemNumber = 0; moveSystemNumber < ARRAY_SIZE(moveStates); ++moveSystemNumber)
{
- // We were not able to skip any moves, however we can skip the remaining segments of this current move
- ToolOffsetInverseTransform(moveState.initialCoords, moveState.currentUserPosition);
- pauseRestorePoint.feedRate = moveState.feedRate;
- pauseRestorePoint.virtualExtruderPosition = moveState.virtualExtruderPosition;
- pauseRestorePoint.filePos = moveState.filePos;
- pauseRestorePoint.proportionDone = moveState.GetProportionDone();
- pauseRestorePoint.initialUserC0 = moveState.initialUserC0;
- pauseRestorePoint.initialUserC1 = moveState.initialUserC1;
+ MovementState& ms = moveStates[moveSystemNumber];
+ // When we use RTOS there is a possible race condition in the following, because we might try to pause when a waiting move has just been added
+ // but before the gcode buffer has been re-initialised ready for the next command. So start a critical section.
+ TaskCriticalSectionLocker lock;
+
+ const bool movesSkipped = reprap.GetMove().LowPowerOrStallPause(moveSystemNumber, ms.pauseRestorePoint);
+ if (movesSkipped)
+ {
+ // The PausePrint call has filled in the restore point with machine coordinates
+ ToolOffsetInverseTransform(ms, ms.pauseRestorePoint.moveCoords, ms.currentUserPosition); // transform the returned coordinates to user coordinates
+ ms.ClearMove();
+ }
+ else if (ms.segmentsLeft != 0 && ms.filePos != noFilePosition)
+ {
+ // We were not able to skip any moves, however we can skip the remaining segments of this current move
+ ToolOffsetInverseTransform(ms, ms.initialCoords, ms.currentUserPosition);
+ ms.pauseRestorePoint.feedRate = ms.feedRate;
+ ms.pauseRestorePoint.virtualExtruderPosition = ms.moveStartVirtualExtruderPosition;
+ ms.pauseRestorePoint.filePos = ms.filePos;
+ ms.pauseRestorePoint.proportionDone = ms.GetProportionDone();
+ ms.pauseRestorePoint.initialUserC0 = ms.initialUserC0;
+ ms.pauseRestorePoint.initialUserC1 = ms.initialUserC1;
#if SUPPORT_LASER || SUPPORT_IOBITS
- pauseRestorePoint.laserPwmOrIoBits = moveState.laserPwmOrIoBits;
+ ms.pauseRestorePoint.laserPwmOrIoBits = ms.laserPwmOrIoBits;
#endif
- ClearMove();
- }
- else
- {
- // We were not able to skip any moves, and if there is a move waiting then we can't skip that one either
- pauseRestorePoint.feedRate = fileGCode->LatestMachineState().feedRate;
- pauseRestorePoint.virtualExtruderPosition = virtualExtruderPosition;
+ ms.ClearMove();
+ }
+ else
+ {
+ // We were not able to skip any moves, and if there is a move waiting then we can't skip that one either
+ ms.pauseRestorePoint.feedRate = FileGCode()->LatestMachineState().feedRate;
+ ms.pauseRestorePoint.virtualExtruderPosition = ms.latestVirtualExtruderPosition;
- pauseRestorePoint.filePos = GetFilePosition(true);
- pauseRestorePoint.proportionDone = 0.0;
+ ms.pauseRestorePoint.filePos = FileGCode()->GetPrintingFilePosition(true); //TODO separate restore point per channel
+ ms.pauseRestorePoint.proportionDone = 0.0;
#if SUPPORT_LASER || SUPPORT_IOBITS
- pauseRestorePoint.laserPwmOrIoBits = moveState.laserPwmOrIoBits;
+ ms.pauseRestorePoint.laserPwmOrIoBits = ms.laserPwmOrIoBits;
#endif
- }
+ }
+
#if HAS_SBC_INTERFACE
- if (reprap.UsingSbcInterface())
- {
- PrintPausedReason reason = platform.IsPowerOk() ? PrintPausedReason::stall : PrintPausedReason::lowVoltage;
- reprap.GetSbcInterface().SetEmergencyPauseReason(pauseRestorePoint.filePos, reason);
- }
+ if (reprap.UsingSbcInterface())
+ {
+ PrintPausedReason reason = platform.IsPowerOk() ? PrintPausedReason::stall : PrintPausedReason::lowVoltage;
+ reprap.GetSbcInterface().SetEmergencyPauseReason(ms.pauseRestorePoint.filePos, reason);
+ }
#endif
- codeQueue->PurgeEntries();
+ ms.codeQueue->PurgeEntries();
- // Replace the paused machine coordinates by user coordinates, which we updated earlier
- for (size_t axis = 0; axis < numVisibleAxes; ++axis)
- {
- pauseRestorePoint.moveCoords[axis] = moveState.currentUserPosition[axis];
- }
+ // Replace the paused machine coordinates by user coordinates, which we updated earlier
+ for (size_t axis = 0; axis < numVisibleAxes; ++axis)
+ {
+ ms.pauseRestorePoint.moveCoords[axis] = ms.currentUserPosition[axis];
+ }
- if (pauseRestorePoint.filePos == noFilePosition)
- {
- // Make sure we expose usable values (which noFilePosition is not)
- pauseRestorePoint.filePos = 0;
+ if (ms.pauseRestorePoint.filePos == noFilePosition)
+ {
+ // Make sure we expose usable values (which noFilePosition is not)
+ ms.pauseRestorePoint.filePos = 0;
+ }
+ ms.pauseRestorePoint.toolNumber = ms.GetCurrentToolNumber();
+ ms.pauseRestorePoint.fanSpeed = ms.virtualFanSpeed;
}
- pauseRestorePoint.toolNumber = reprap.GetCurrentToolNumber();
- pauseRestorePoint.fanSpeed = lastDefaultFanSpeed;
+
pauseState = PauseState::paused;
return true;
@@ -1153,9 +1222,9 @@ bool GCodes::LowVoltagePause() noexcept
// Run the auto-pause script
if (powerFailScript != nullptr)
{
- autoPauseGCode->PutAndDecode(powerFailScript);
+ AutoPauseGCode()->PutAndDecode(powerFailScript);
}
- autoPauseGCode->SetState(GCodeState::powerFailPausing1);
+ AutoPauseGCode()->SetState(GCodeState::powerFailPausing1);
isPowerFailPaused = true;
// Don't do any more here, we want the auto pause thread to run as soon as possible
@@ -1195,7 +1264,9 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
}
else
{
+ const MovementState& ms = GetPrimaryMovementState(); //TODO save resume info for all movement states
String<StringLength256> buf;
+ RestorePoint& pauseRestorePoint = ms.pauseRestorePoint; //TODO handle pausing when multiple motion system are active
// Write the header comment
buf.printf("; File \"%s\" resume print after %s", printingFilename, (wasPowerFailure) ? "power failure" : "print paused");
@@ -1218,7 +1289,7 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
buf.copy("G92");
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- const float totalOffset = currentBabyStepOffsets[axis] - GetCurrentToolOffset(axis);
+ const float totalOffset = currentBabyStepOffsets[axis] - ms.GetCurrentToolOffset(axis);
buf.catf(" %c%.3f", axisLetters[axis], (double)(pauseRestorePoint.moveCoords[axis] - totalOffset));
}
buf.cat("\nG60 S1\n"); // save the coordinates as restore point 1 too
@@ -1226,7 +1297,7 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
}
if (ok)
{
- ok = reprap.WriteToolSettings(f); // set tool temperatures, tool mix ratios etc. and select the current tool without running tool change files
+ ok = WriteToolSettings(f, ms); // set tool temperatures, tool mix ratios etc. and select the current tool without running tool change files
}
if (ok)
{
@@ -1245,11 +1316,14 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
}
// Now that we have homed, we can run the tool change files for the current tool
- const Tool * const ct = reprap.GetCurrentTool();
- if (ok && ct != nullptr)
+ if (ok)
{
- buf.printf("T-1 P0\nT%u P6\n", ct->Number()); // deselect the current tool without running tfree, and select it running tpre and tpost
- ok = f->Write(buf.c_str()); // write tool selection
+ const int toolNumber = ms.GetCurrentToolNumber();
+ if (toolNumber >= 0)
+ {
+ buf.printf("T-1 P0\nT%d P6\n", toolNumber); // deselect the current tool without running tfree, and select it running tpre and tpost
+ ok = f->Write(buf.c_str()); // write tool selection
+ }
}
#if SUPPORT_WORKPLACE_COORDINATES
@@ -1262,13 +1336,14 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
if (ok)
{
// Switch to the correct workplace. 'currentCoordinateSystem' is 0-based.
- if (moveState.currentCoordinateSystem <= 5)
+ //TODO handle multiple motion systems!
+ if (ms.currentCoordinateSystem <= 5)
{
- buf.printf("G%u\n", 54 + moveState.currentCoordinateSystem);
+ buf.printf("G%u\n", 54 + ms.currentCoordinateSystem);
}
else
{
- buf.printf("G59.%u\n", moveState.currentCoordinateSystem - 5);
+ buf.printf("G59.%u\n", ms.currentCoordinateSystem - 5);
}
ok = f->Write(buf.c_str());
}
@@ -1284,8 +1359,7 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
ok = f->Write(buf.c_str());
}
#endif
-
- if (ok && fileGCode->OriginalMachineState().volumetricExtrusion)
+ if (ok && FileGCode()->OriginalMachineState().volumetricExtrusion)
{
buf.copy("M200 ");
char c = 'D';
@@ -1299,14 +1373,16 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
}
if (ok)
{
- buf.printf("M106 S%.2f\n", (double)lastDefaultFanSpeed);
+ buf.printf("M106 S%.2f\n", (double)ms.virtualFanSpeed);
ok = f->Write(buf.c_str()) // set the speed of the print fan after we have selected the tool
&& reprap.GetFansManager().WriteFanSettings(f); // set the speeds of all non-thermostatic fans after setting the default fan speed
}
if (ok)
{
- buf.printf("M116\nG92 E%.5f\n%s\n", (double)virtualExtruderPosition, (fileGCode->OriginalMachineState().drivesRelative) ? "M83" : "M82");
- ok = f->Write(buf.c_str()); // write virtual extruder position and absolute/relative extrusion flag
+ buf.printf("M116\nG92 E%.5f\n%s\n%s\n", (double)ms.latestVirtualExtruderPosition,
+ (FileGCode()->OriginalMachineState().drivesRelative) ? "M83" : "M82",
+ (FileGCode()->OriginalMachineState().inverseTimeMode) ? "G93" : "G94");
+ ok = f->Write(buf.c_str()); // write virtual extruder position, absolute/relative extrusion flag, and inverse time mode/normal mode flag
}
if (ok)
{
@@ -1314,7 +1390,7 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
}
if (ok)
{
- const unsigned int selectedPlane = fileGCode->OriginalMachineState().selectedPlane;
+ const unsigned int selectedPlane = FileGCode()->OriginalMachineState().selectedPlane;
buf.printf("G%u\nM23 \"%s\"\nM26 S%" PRIu32, selectedPlane + 17, printingFilename, pauseRestorePoint.filePos);
if (pauseRestorePoint.proportionDone > 0.0)
{
@@ -1366,7 +1442,7 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
}
if (ok)
{
- buf.printf("%s\nM24\n", (fileGCode->OriginalMachineState().usingInches) ? "G20" : "G21");
+ buf.printf("%s\nM24\n", (FileGCode()->OriginalMachineState().usingInches) ? "G20" : "G21");
ok = f->Write(buf.c_str()); // restore inches/mm and resume printing
}
if (!f->Close())
@@ -1391,9 +1467,17 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure) noexcept
void GCodes::Diagnostics(MessageType mtype) noexcept
{
platform.Message(mtype, "=== GCodes ===\n");
- platform.MessageF(mtype, "Segments left: %u\n", moveState.segmentsLeft);
- const GCodeBuffer * const movementOwner = resourceOwners[MoveResource];
- platform.MessageF(mtype, "Movement lock held by %s\n", (movementOwner == nullptr) ? "null" : movementOwner->GetChannel().ToString());
+ String<StringLength50> text;
+ for (unsigned int i = 0; i < NumMovementSystems; ++i)
+ {
+ const GCodeBuffer * const movementOwner = resourceOwners[MoveResourceBase + i];
+ if (i != 0)
+ {
+ text.cat(", ");
+ }
+ text.cat((movementOwner == nullptr) ? "null" : movementOwner->GetChannel().ToString());
+ }
+ platform.MessageF(mtype, "Movement locks held by %s\n", text.c_str());
for (GCodeBuffer *gb : gcodeSources)
{
@@ -1403,12 +1487,20 @@ void GCodes::Diagnostics(MessageType mtype) noexcept
}
}
- codeQueue->Diagnostics(mtype);
+ for (size_t i = 0; i < ARRAY_SIZE(moveStates); ++i)
+ {
+ moveStates[i].Diagnostics(mtype, i);
+ }
}
// Lock movement and wait for pending moves to finish.
-// As a side-effect it loads moveBuffer with the last position and feedrate for you.
-bool GCodes::LockMovementAndWaitForStandstill(GCodeBuffer& gb) noexcept
+// Return true if successful, false if we need to try again later.
+// As a side-effect it updates the user coordinates from the machine coordinates.
+bool GCodes::LockMovementAndWaitForStandstill(GCodeBuffer& gb
+#if SUPPORT_ASYNC_MOVES
+ , bool sync
+#endif
+ ) noexcept
{
// Lock movement to stop another source adding moves to the queue
if (!LockMovement(gb))
@@ -1416,21 +1508,83 @@ bool GCodes::LockMovementAndWaitForStandstill(GCodeBuffer& gb) noexcept
return false;
}
- // Last one gone?
- if (moveState.segmentsLeft != 0)
+ MovementState& ms = GetMovementState(gb);
+ if (ms.segmentsLeft != 0) // has the last move generated been fully transferred to the movement queue?
{
- return false;
+ return false; // if no
}
- // Wait for all the queued moves to stop so we get the actual last position
- if (!reprap.GetMove().WaitingForAllMovesFinished())
+ switch (gb.GetChannel().ToBaseType())
{
- return false;
- }
+ case GCodeChannel::Queue:
+ case GCodeChannel::Queue2:
+ break;
- if (&gb != queuedGCode && !IsCodeQueueIdle()) // wait for deferred command queue to catch up
- {
- return false;
+#if SUPPORT_ASYNC_MOVES
+ case GCodeChannel::File:
+ if (!reprap.GetMove().WaitingForAllMovesFinished(0))
+ {
+ return false;
+ }
+ if (!(QueuedGCode()->IsIdle() && moveStates[0].codeQueue->IsIdle()))
+ {
+ return false;
+ }
+
+ // Now that we know that pending commands for this queue are completed, we can try to sync with other GCode buffers
+ if (sync && !gb.ExecutingAll() && File2GCode()->IsDoingFile())
+ {
+ const bool ret = SyncWith(gb, *File2GCode());
+ if (ret)
+ {
+ gb.MotionStopped();
+ }
+ //if (!ret) { debugPrintf("Lock wait 7, queue %u\n", gb.GetQueueNumberToLock()); }
+ return ret;
+ }
+ break;
+
+ case GCodeChannel::File2:
+ if (!reprap.GetMove().WaitingForAllMovesFinished(1))
+ {
+ return false;
+ }
+ if (!(Queue2GCode()->IsIdle() && moveStates[1].codeQueue->IsIdle()))
+ {
+ return false;
+ }
+
+ // Now that we know that pending commands for this queue are completed, we can try to sync with other GCode buffers
+ if (sync && !gb.ExecutingAll() && FileGCode()->IsDoingFile())
+ {
+ const bool ret = SyncWith(gb, *FileGCode());
+ if (ret)
+ {
+ gb.MotionStopped();
+ }
+ return ret;
+ }
+ break;
+#endif
+
+ default:
+ if ( !reprap.GetMove().WaitingForAllMovesFinished(0)
+#if SUPPORT_ASYNC_MOVES
+ || !reprap.GetMove().WaitingForAllMovesFinished(1)
+#endif
+ )
+ {
+ return false;
+ }
+
+ if ( !(QueuedGCode()->IsIdle() && moveStates[0].codeQueue->IsIdle())
+#if SUPPORT_ASYNC_MOVES
+ && !(Queue2GCode()->IsIdle() && moveStates[1].codeQueue->IsIdle())
+#endif
+ )
+ {
+ return false;
+ }
}
gb.MotionStopped(); // must do this after we have finished waiting, so that we don't stop waiting when executing G4
@@ -1438,12 +1592,34 @@ bool GCodes::LockMovementAndWaitForStandstill(GCodeBuffer& gb) noexcept
if (RTOSIface::GetCurrentTask() == Tasks::GetMainTask())
{
// Get the current positions. These may not be the same as the ones we remembered from last time if we just did a special move.
+#if SUPPORT_ASYNC_MOVES
+ // Get the position of all axes by combining positions from the queues
+ Move& move = reprap.GetMove();
+ float coords[MaxAxes];
+ move.GetPartialMachinePosition(coords, AxesBitmap::MakeLowestNBits(numTotalAxes), 0);
+ move.GetPartialMachinePosition(coords, moveStates[1].axesAndExtrudersOwned, 1);
+ memcpyf(moveStates[0].coords, coords, MaxAxes);
+ memcpyf(moveStates[1].coords, coords, MaxAxes);
+ move.InverseAxisAndBedTransform(moveStates[0].coords, moveStates[0].currentTool);
+ move.InverseAxisAndBedTransform(moveStates[1].coords, moveStates[1].currentTool);
+ UpdateUserPositionFromMachinePosition(gb, moveStates[0]); //TODO problem when using coordinate rotation!
+ UpdateUserPositionFromMachinePosition(gb, moveStates[1]);
+ move.SetNewPosition(coords, true, 0);
+ move.SetNewPosition(coords, true, 1);
+
+ // Release all axes and extruders
+ axesAndExtrudersMoved.Clear();
+ moveStates[0].axesAndExtrudersOwned.Clear();
+ moveStates[1].axesAndExtrudersOwned.Clear();
+ collisionChecker.ResetPositions(moveStates[0].coords);
+#else
UpdateCurrentUserPosition(gb);
+#endif
}
else
{
// Cannot update the user position from external tasks. Do it later
- updateUserPositionGb = &gb;
+ ms.updateUserPositionGb = &gb;
}
return true;
}
@@ -1473,47 +1649,62 @@ void GCodes::Pop(GCodeBuffer& gb, bool withinSameFile) noexcept
// Set up the extrusion and feed rate of a move for the Move class
// 'moveBuffer.moveType' and 'moveBuffer.isCoordinated' must be set up before calling this
// 'isPrintingMove' is true if there is any axis movement
-// Returns nullptr if this gcode is valid so far, or an error message if it should be discarded
-const char * GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, bool isPrintingMove) THROWS(GCodeException)
+void GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, MovementState& ms, bool isPrintingMove) THROWS(GCodeException)
{
// Deal with feed rate, also determine whether M220 and M221 speed and extrusion factors apply to this move
- if (moveState.isCoordinated || machineType == MachineType::fff)
+ if (ms.isCoordinated || machineType == MachineType::fff)
{
- moveState.applyM220M221 = (moveState.moveType == 0 && isPrintingMove && !gb.IsDoingFileMacro());
- if (gb.Seen(feedrateLetter))
+ ms.applyM220M221 = (ms.moveType == 0 && isPrintingMove && !gb.IsDoingFileMacro());
+ ms.inverseTimeMode = gb.LatestMachineState().inverseTimeMode;
+ if (ms.inverseTimeMode)
{
- gb.LatestMachineState().feedRate = gb.GetSpeed(); // update requested speed, not allowing for speed factor
+ if (!gb.Seen(feedrateLetter))
+ {
+ gb.ThrowGCodeException("Feed rate must be specified with every move when using inverse time mode");
+ }
+ const float feedRate = (StepClockRate * 60)/gb.GetPositiveFValue(); // get the requested move duration in step clocks
+ ms.feedRate = (ms.applyM220M221)
+ ? feedRate/ms.speedFactor
+ : feedRate;
}
- moveState.feedRate = (moveState.applyM220M221)
- ? speedFactor * gb.LatestMachineState().feedRate
+ else
+ {
+ if (gb.Seen(feedrateLetter))
+ {
+ gb.LatestMachineState().feedRate = gb.GetSpeed(); // update requested speed in mm per step clock, not allowing for speed factor
+ }
+ ms.feedRate = (ms.applyM220M221)
+ ? gb.LatestMachineState().feedRate * ms.speedFactor
: gb.LatestMachineState().feedRate;
- moveState.usingStandardFeedrate = true;
+ }
+ ms.usingStandardFeedrate = true;
}
else
{
- moveState.applyM220M221 = false;
- moveState.feedRate = ConvertSpeedFromMmPerMin(MaximumG0FeedRate); // use maximum feed rate, the M203 parameters will limit it
- moveState.usingStandardFeedrate = false;
+ ms.applyM220M221 = false;
+ ms.feedRate = ConvertSpeedFromMmPerMin(MaximumG0FeedRate); // use maximum feed rate, the M203 parameters will limit it
+ ms.usingStandardFeedrate = false;
}
// Zero every extruder drive as some drives may not be moved
for (size_t drive = numTotalAxes; drive < MaxAxesPlusExtruders; drive++)
{
- moveState.coords[drive] = 0.0;
+ ms.coords[drive] = 0.0;
}
- moveState.hasPositiveExtrusion = false;
- moveState.virtualExtruderPosition = virtualExtruderPosition; // save this before we update it
+ ms.hasPositiveExtrusion = false;
+ ms.moveStartVirtualExtruderPosition = ms.latestVirtualExtruderPosition; // save this before we update it
+ AxesBitmap logicalDrivesMoving;
ExtrudersBitmap extrudersMoving;
// Check if we are extruding
if (gb.Seen(extrudeLetter)) // DC 2018-08-07: at E3D's request, extrusion is now recognised even on uncoordinated moves
{
// Check that we have a tool to extrude with
- Tool* const tool = reprap.GetCurrentTool();
+ const Tool* const tool = ms.currentTool;
if (tool == nullptr)
{
displayNoToolWarning = true;
- return nullptr;
+ return;
}
const size_t eMoveCount = tool->DriveCount();
@@ -1535,20 +1726,20 @@ const char * GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, bool isP
}
else
{
- requestedExtrusionAmount = moveArg - virtualExtruderPosition;
- virtualExtruderPosition = moveArg;
+ requestedExtrusionAmount = moveArg - ms.latestVirtualExtruderPosition;
+ ms.latestVirtualExtruderPosition = moveArg;
}
if (requestedExtrusionAmount > 0.0)
{
- moveState.hasPositiveExtrusion = true;
+ ms.hasPositiveExtrusion = true;
}
// rawExtruderTotal is used to calculate print progress, so it must be based on the requested extrusion from the slicer
// before accounting for mixing, extrusion factor etc.
// We used to have 'isPrintingMove &&' in the condition too, but this excluded wipe-while-retracting moves, so it gave wrong results for % print complete.
// We still exclude extrusion during tool changing and other macros, because that is extrusion not known to the slicer.
- if (moveState.moveType == 0 && !gb.IsDoingFileMacro())
+ if (ms.moveType == 0 && !gb.IsDoingFileMacro())
{
rawExtruderTotal += requestedExtrusionAmount;
}
@@ -1566,21 +1757,22 @@ const char * GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, bool isP
{
extrusionAmount *= volumetricExtrusionFactors[extruder];
}
- if (eDrive == 0 && moveState.moveType == 0 && !gb.IsDoingFileMacro())
+ if (eDrive == 0 && ms.moveType == 0 && !gb.IsDoingFileMacro())
{
rawExtruderTotalByDrive[extruder] += extrusionAmount;
}
- moveState.coords[ExtruderToLogicalDrive(extruder)] = (moveState.applyM220M221)
- ? extrusionAmount * extrusionFactors[extruder]
- : extrusionAmount;
+ ms.coords[ExtruderToLogicalDrive(extruder)] = (ms.applyM220M221)
+ ? extrusionAmount * extrusionFactors[extruder]
+ : extrusionAmount;
extrudersMoving.SetBit(extruder);
+ logicalDrivesMoving.SetBit(ExtruderToLogicalDrive(extruder));
}
}
- if (!isPrintingMove && moveState.usingStandardFeedrate)
+ if (!isPrintingMove && ms.usingStandardFeedrate)
{
// For E3D: If the total mix ratio is greater than 1.0 then we should scale the feed rate accordingly, e.g. for dual serial extruder drives
- moveState.feedRate *= totalMix;
+ ms.feedRate *= totalMix;
}
}
else
@@ -1597,7 +1789,7 @@ const char * GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, bool isP
{
if (extrusionAmount > 0.0)
{
- moveState.hasPositiveExtrusion = true;
+ ms.hasPositiveExtrusion = true;
}
if (gb.LatestMachineState().volumetricExtrusion)
@@ -1605,74 +1797,75 @@ const char * GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, bool isP
extrusionAmount *= volumetricExtrusionFactors[extruder];
}
- if (eDrive < mc && moveState.moveType == 0 && !gb.IsDoingFileMacro())
+ if (eDrive < mc && ms.moveType == 0 && !gb.IsDoingFileMacro())
{
rawExtruderTotalByDrive[extruder] += extrusionAmount;
rawExtruderTotal += extrusionAmount;
}
- moveState.coords[ExtruderToLogicalDrive(extruder)] = (moveState.applyM220M221)
- ? extrusionAmount * extrusionFactors[extruder]
- : extrusionAmount;
+ ms.coords[ExtruderToLogicalDrive(extruder)] = (ms.applyM220M221)
+ ? extrusionAmount * extrusionFactors[extruder]
+ : extrusionAmount;
extrudersMoving.SetBit(extruder);
+ logicalDrivesMoving.SetBit(ExtruderToLogicalDrive(extruder));
}
}
}
else
{
- return "Multiple E parameters in G1 commands are not supported in absolute extrusion mode";
+ gb.ThrowGCodeException("Multiple E parameters in G1 commands are not supported in absolute extrusion mode");
}
}
}
}
- if (moveState.moveType == 1 || moveState.moveType == 4)
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, logicalDrivesMoving);
+#endif
+
+ if (ms.moveType == 1 || ms.moveType == 4)
{
if (!platform.GetEndstops().EnableExtruderEndstops(extrudersMoving))
{
- return "Failed to enable extruder endstops";
+ gb.ThrowGCodeException("Failed to enable extruder endstops");
}
}
-
- return nullptr;
}
// Check that enough axes have been homed, returning true if insufficient axes homed
-bool GCodes::CheckEnoughAxesHomed(AxesBitmap axesMoved) noexcept
+bool GCodes::CheckEnoughAxesHomed(AxesBitmap axesToMove) noexcept
{
- return (reprap.GetMove().GetKinematics().MustBeHomedAxes(axesMoved, noMovesBeforeHoming) & ~axesVirtuallyHomed).IsNonEmpty();
+ return (reprap.GetMove().GetKinematics().MustBeHomedAxes(axesToMove, noMovesBeforeHoming) & ~axesVirtuallyHomed).IsNonEmpty();
}
-// Execute a straight move
-// If not ready, return false
-// If we can't execute the move, return true with 'err' set to the error message
-// Else return true with 'err' left alone (it is set to nullptr on entry)
-// We have already acquired the movement lock and waited for the previous move to be taken.
-bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& err) THROWS(GCodeException)
+// Execute a straight move. We have already acquired the movement lock and waited for the previous move to be taken.
+// Return false if we can't execute it yet, throw an exception if we can't execute it at all, and return true if we have queued it.
+bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeException)
{
- if (moveFractionToSkip > 0.0)
+ MovementState& ms = GetMovementState(gb);
+ if (ms.moveFractionToSkip > 0.0)
{
- moveState.initialUserC0 = restartInitialUserC0;
- moveState.initialUserC1 = restartInitialUserC1;
+ ms.initialUserC0 = ms.restartInitialUserC0;
+ ms.initialUserC1 = ms.restartInitialUserC1;
}
else
{
const unsigned int selectedPlane = gb.LatestMachineState().selectedPlane;
- moveState.initialUserC0 = moveState.currentUserPosition[(selectedPlane == 2) ? Y_AXIS : X_AXIS];
- moveState.initialUserC1 = moveState.currentUserPosition[(selectedPlane == 0) ? Y_AXIS : Z_AXIS];
+ ms.initialUserC0 = ms.currentUserPosition[(selectedPlane == 2) ? Y_AXIS : X_AXIS];
+ ms.initialUserC1 = ms.currentUserPosition[(selectedPlane == 0) ? Y_AXIS : Z_AXIS];
}
// Set up default move parameters
- moveState.isCoordinated = isCoordinated;
- moveState.checkEndstops = false;
- moveState.reduceAcceleration = false;
- moveState.moveType = 0;
- moveState.tool = reprap.GetCurrentTool();
- moveState.usePressureAdvance = false;
+ ms.isCoordinated = isCoordinated;
+ ms.checkEndstops = false;
+ ms.reduceAcceleration = false;
+ ms.movementTool = ms.currentTool;
+ ms.moveType = 0;
+ ms.usePressureAdvance = false;
axesToSenseLength.Clear();
// Check to see if the move is a 'homing' move that endstops are checked on.
// We handle H1 parameters affecting extrusion elsewhere.
- if (gb.Seen('H') || (machineType != MachineType::laser && gb.Seen('S')))
+ if (gb.Seen('H'))
{
const int ival = gb.GetIValue();
if (ival >= 1 && ival <= 4)
@@ -1681,28 +1874,23 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
{
return false;
}
- moveState.moveType = ival;
- moveState.tool = nullptr;
- }
- if (!gb.Seen('H'))
- {
- platform.Message(MessageType::WarningMessage, "Obsolete use of S parameter on G1 command. Use H parameter instead.\n");
+ ms.moveType = ival;
+ ms.movementTool = nullptr;
}
}
// Check for 'R' parameter to move relative to a restore point
const RestorePoint * rp = nullptr;
- if (moveState.moveType == 0 && gb.Seen('R'))
+ if (ms.moveType == 0 && gb.Seen('R'))
{
const uint32_t rParam = gb.GetUIValue();
- if (rParam < ARRAY_SIZE(numberedRestorePoints))
+ if (rParam < NumVisibleRestorePoints)
{
- rp = &numberedRestorePoints[rParam];
+ rp = &ms.restorePoints[rParam];
}
else
{
- err = "G0/G1: bad restore point number";
- return true;
+ gb.ThrowGCodeException("G0/G1: bad restore point number");
}
}
@@ -1710,18 +1898,18 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
#if SUPPORT_LASER || SUPPORT_IOBITS
if (rp != nullptr)
{
- moveState.laserPwmOrIoBits = rp->laserPwmOrIoBits;
+ ms.laserPwmOrIoBits = rp->laserPwmOrIoBits;
}
# if SUPPORT_LASER
else if (machineType == MachineType::laser)
{
if (gb.Seen('S'))
{
- moveState.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
+ ms.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
}
- else if (moveState.moveType != 0)
+ else if (ms.moveType != 0)
{
- moveState.laserPwmOrIoBits.laserPwm = 0; // it's a homing move or similar, so turn the laser off
+ ms.laserPwmOrIoBits.laserPwm = 0; // it's a homing move or similar, so turn the laser off
}
else if (laserPowerSticky)
{
@@ -1730,7 +1918,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
}
else
{
- moveState.laserPwmOrIoBits.laserPwm = 0;
+ ms.laserPwmOrIoBits.laserPwm = 0;
}
}
# endif
@@ -1740,7 +1928,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
// Update the iobits parameter
if (gb.Seen('P'))
{
- moveState.laserPwmOrIoBits.ioBits = (IoBits_t)gb.GetIValue();
+ ms.laserPwmOrIoBits.ioBits = (IoBits_t)gb.GetIValue();
}
else
{
@@ -1750,20 +1938,20 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
# endif
#endif
- if (moveState.moveType != 0)
+ if (ms.moveType != 0)
{
// This may be a raw motor move, in which case we need the current raw motor positions in moveBuffer.coords.
// If it isn't a raw motor move, it will still be applied without axis or bed transform applied,
// so make sure the initial coordinates don't have those either to avoid unwanted Z movement.
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, moveState.moveType, reprap.GetCurrentTool());
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, ms.moveType, ms.currentTool);
}
// Set up the initial coordinates
- memcpyf(moveState.initialCoords, moveState.coords, numVisibleAxes);
+ memcpyf(ms.initialCoords, ms.coords, numVisibleAxes);
// Save the current position, we need it possibly later
float initialUserPosition[MaxAxes];
- memcpyf(initialUserPosition, moveState.currentUserPosition, numVisibleAxes);
+ memcpyf(initialUserPosition, ms.currentUserPosition, numVisibleAxes);
AxesBitmap axesMentioned;
for (size_t axis = 0; axis < numVisibleAxes; axis++)
@@ -1771,128 +1959,157 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
if (gb.Seen(axisLetters[axis]))
{
// If it is a special move on a delta, movement must be relative.
- if (moveState.moveType != 0 && !gb.LatestMachineState().axesRelative && reprap.GetMove().GetKinematics().GetKinematicsType() == KinematicsType::linearDelta)
+ if (ms.moveType != 0 && !gb.LatestMachineState().axesRelative && reprap.GetMove().GetKinematics().GetKinematicsType() == KinematicsType::linearDelta)
{
- err = "G0/G1: attempt to move individual motors of a delta machine to absolute positions";
- return true;
+ gb.ThrowGCodeException("G0/G1: attempt to move individual motors of a delta machine to absolute positions");
}
axesMentioned.SetBit(axis);
const float moveArg = gb.GetDistance();
- if (moveState.moveType != 0)
+ if (ms.moveType != 0)
{
// Special moves update the move buffer directly, bypassing the user coordinates
if (gb.LatestMachineState().axesRelative)
{
- moveState.coords[axis] += moveArg * (1.0 - moveFractionToSkip);
+ ms.coords[axis] += moveArg * (1.0 - ms.moveFractionToSkip);
}
else
{
- moveState.coords[axis] = moveArg;
+ ms.coords[axis] = moveArg;
}
}
else if (rp != nullptr)
{
- moveState.currentUserPosition[axis] = moveArg + rp->moveCoords[axis];
+ ms.currentUserPosition[axis] = moveArg + rp->moveCoords[axis];
// When a restore point is being used (G1 R parameter) then we used to set any coordinates that were not mentioned to the restore point values.
// But that causes issues for tool change on IDEX machines because we end up restoring the U axis when we shouldn't.
// So we no longer do that, and the user must mention any axes that he wants restored e.g. G1 R2 X0 Y0.
}
else if (gb.LatestMachineState().axesRelative)
{
- moveState.currentUserPosition[axis] += moveArg * (1.0 - moveFractionToSkip);
+ ms.currentUserPosition[axis] += moveArg * (1.0 - ms.moveFractionToSkip);
}
else if (gb.LatestMachineState().g53Active)
{
- moveState.currentUserPosition[axis] = moveArg + GetCurrentToolOffset(axis); // g53 ignores tool offsets as well as workplace coordinates
+ ms.currentUserPosition[axis] = moveArg + ms.GetCurrentToolOffset(axis); // g53 ignores tool offsets as well as workplace coordinates
}
else if (gb.LatestMachineState().runningSystemMacro)
{
- moveState.currentUserPosition[axis] = moveArg; // don't apply workplace offsets to commands in system macros
+ ms.currentUserPosition[axis] = moveArg; // don't apply workplace offsets to commands in system macros
}
else
{
- moveState.currentUserPosition[axis] = moveArg + GetWorkplaceOffset(axis);
+ ms.currentUserPosition[axis] = moveArg + GetWorkplaceOffset(gb, axis);
}
}
}
- // Check enough axes have been homed
- switch (moveState.moveType)
+ if (ms.moveType == 0)
{
- case 0:
- if (!doingManualBedProbe && CheckEnoughAxesHomed(axesMentioned))
+#if SUPPORT_COORDINATE_ROTATION
+ // Update the list of axes mentioned to allow for cross coupling between X and Y
+ if (g68Angle != 0.0 && gb.DoingCoordinateRotation())
{
- err = "G0/G1: insufficient axes homed";
- return true;
+ const AxesBitmap xAndY = AxesBitmap::MakeFromBits(X_AXIS, Y_AXIS);
+ if (axesMentioned.Intersects(xAndY))
+ {
+ axesMentioned |= xAndY; // if either X or Y is moving and we are rotating coordinates, both are moving
+ }
}
- break;
-
- case 3:
- axesToSenseLength = axesMentioned & AxesBitmap::MakeLowestNBits(numTotalAxes);
- // no break
- case 1:
- case 4:
+#endif
+ // Check and record which real axes this movement system is moving
+ AxesBitmap realAxesMoving;
+ if (ms.currentTool == nullptr)
{
- bool reduceAcceleration;
- if (!platform.GetEndstops().EnableAxisEndstops(axesMentioned & AxesBitmap::MakeLowestNBits(numTotalAxes), moveState.moveType == 1, reduceAcceleration))
+ realAxesMoving = axesMentioned;
+ }
+ else
+ {
+ realAxesMoving = axesMentioned & ~AxesBitmap::MakeFromBits(X_AXIS, Y_AXIS);
+ if (axesMentioned.IsBitSet(X_AXIS))
{
- err = "Failed to enable endstops";
- return true;
+ realAxesMoving |= ms.currentTool->GetXAxisMap();
+ }
+ if (axesMentioned.IsBitSet(Y_AXIS))
+ {
+ realAxesMoving |= ms.currentTool->GetYAxisMap();
}
- moveState.reduceAcceleration = reduceAcceleration;
}
- moveState.checkEndstops = true;
- break;
- case 2:
- default:
- break;
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, realAxesMoving);
+#endif
+ if (!doingManualBedProbe && CheckEnoughAxesHomed(axesMentioned))
+ {
+ gb.ThrowGCodeException("G0/G1: insufficient axes homed");
+ }
}
-
- err = LoadExtrusionAndFeedrateFromGCode(gb, axesMentioned.IsNonEmpty()); // for type 1 moves, this must be called after calling EnableAxisEndstops, because EnableExtruderEndstop assumes that
- if (err != nullptr)
+ else
{
- return true;
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, axesMentioned);
+#endif
+ switch (ms.moveType)
+ {
+ case 3:
+ axesToSenseLength = axesMentioned & AxesBitmap::MakeLowestNBits(numTotalAxes);
+ // no break
+ case 1:
+ case 4:
+ {
+ bool reduceAcceleration;
+ if (!platform.GetEndstops().EnableAxisEndstops(axesMentioned & AxesBitmap::MakeLowestNBits(numTotalAxes), ms.moveType == 1, reduceAcceleration))
+ {
+ gb.ThrowGCodeException("Failed to enable endstops");
+ }
+ ms.reduceAcceleration = reduceAcceleration;
+ }
+ ms.checkEndstops = true;
+ break;
+
+ case 2:
+ default:
+ break;
+ }
}
- const bool isPrintingMove = moveState.hasPositiveExtrusion && axesMentioned.IsNonEmpty();
- if (buildObjects.IsFirstMoveSincePrintingResumed()) // if this is the first move after skipping an object
+ LoadExtrusionAndFeedrateFromGCode(gb, ms, axesMentioned.IsNonEmpty()); // for type 1 moves, this must be called after calling EnableAxisEndstops, because EnableExtruderEndstop assumes that
+
+ const bool isPrintingMove = ms.hasPositiveExtrusion && axesMentioned.IsNonEmpty();
+ if (ms.IsFirstMoveSincePrintingResumed()) // if this is the first move after skipping an object
{
if (isPrintingMove)
{
if (TravelToStartPoint(gb)) // don't start a printing move from the wrong place
{
- buildObjects.DoneMoveSincePrintingResumed();
+ ms.DoneMoveSincePrintingResumed();
}
return false;
}
else if (axesMentioned.IsNonEmpty()) // don't count G1 Fxxx as a travel move
{
- buildObjects.DoneMoveSincePrintingResumed();
+ ms.DoneMoveSincePrintingResumed();
}
}
-#if TRACK_OBJECT_NAMES
if (isPrintingMove)
{
// Update the object coordinates limits. For efficiency, we only update the final coordinate.
// Except in the case of a straight line that is only one extrusion width wide, this is sufficient.
- buildObjects.UpdateObjectCoordinates(moveState.currentUserPosition, axesMentioned);
+ buildObjects.UpdateObjectCoordinates(ms.currentObjectNumber, ms.currentUserPosition, axesMentioned);
}
-#endif
// Set up the move. We must assign segmentsLeft last, so that when Move runs as a separate task the move won't be picked up by the Move process before it is complete.
// Note that if this is an extruder-only move, we don't do axis movements to allow for tool offset changes, we defer those until an axis moves.
- if (moveState.moveType != 0)
+ if (ms.moveType != 0)
{
// It's a raw motor move, so do it in a single segment and wait for it to complete
- moveState.totalSegments = 1;
+ ms.totalSegments = 1;
gb.SetState(GCodeState::waitingForSpecialMoveToComplete);
}
else if (axesMentioned.IsEmpty())
{
- moveState.totalSegments = 1;
+ ms.totalSegments = 1; // it's an extruder only move
}
else
{
@@ -1900,15 +2117,22 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
if (g68Angle != 0.0 && gb.DoingCoordinateRotation())
{
float coords[MaxAxes];
- memcpyf(coords, moveState.currentUserPosition, MaxAxes);
+ memcpyf(coords, ms.currentUserPosition, MaxAxes);
RotateCoordinates(g68Angle, coords);
- ToolOffsetTransform(coords, moveState.coords, axesMentioned);
+ ToolOffsetTransform(ms, coords, ms.coords, axesMentioned);
}
else
#endif
{
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords, axesMentioned); // apply tool offset, baby stepping, Z hop and axis scaling
+ ToolOffsetTransform(ms, axesMentioned);
}
+ // apply tool offset, baby stepping, Z hop and axis scaling
+#if SUPPORT_ASYNC_MOVES
+ if (!collisionChecker.UpdatePositions(ms.coords))
+ {
+ gb.ThrowGCodeException("potential collision detected");
+ }
+#endif
AxesBitmap effectiveAxesHomed = axesVirtuallyHomed;
if (doingManualBedProbe)
@@ -1916,17 +2140,16 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
effectiveAxesHomed.ClearBit(Z_AXIS); // if doing a manual Z probe, don't limit the Z movement
}
- const LimitPositionResult lp = reprap.GetMove().GetKinematics().LimitPosition(moveState.coords, moveState.initialCoords, numVisibleAxes, effectiveAxesHomed, moveState.isCoordinated, limitAxes);
+ const LimitPositionResult lp = reprap.GetMove().GetKinematics().LimitPosition(ms.coords, ms.initialCoords, numVisibleAxes, effectiveAxesHomed, ms.isCoordinated, limitAxes);
switch (lp)
{
case LimitPositionResult::adjusted:
case LimitPositionResult::adjustedAndIntermediateUnreachable:
if (machineType != MachineType::fff)
{
- err = "G0/G1: target position outside machine limits"; // it's a laser or CNC so this is a definite error
- return true;
+ gb.ThrowGCodeException("G0/G1: target position outside machine limits"); // it's a laser or CNC so this is a definite error
}
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // make sure the limits are reflected in the user position
+ ToolOffsetInverseTransform(ms); // make sure the limits are reflected in the user position
if (lp == LimitPositionResult::adjusted)
{
break; // we can reach the intermediate positions, so nothing more to do
@@ -1934,24 +2157,23 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
// no break
case LimitPositionResult::intermediateUnreachable:
- if ( moveState.isCoordinated
- && ( (machineType == MachineType::fff && !moveState.hasPositiveExtrusion)
+ if ( ms.isCoordinated
+ && ( (machineType == MachineType::fff && !ms.hasPositiveExtrusion)
#if SUPPORT_LASER || SUPPORT_IOBITS
- || (machineType == MachineType::laser && moveState.laserPwmOrIoBits.laserPwm == 0)
+ || (machineType == MachineType::laser && ms.laserPwmOrIoBits.laserPwm == 0)
#endif
)
)
{
// It's a coordinated travel move on a 3D printer or laser cutter, so see whether an uncoordinated move will work
- const LimitPositionResult lp2 = reprap.GetMove().GetKinematics().LimitPosition(moveState.coords, moveState.initialCoords, numVisibleAxes, effectiveAxesHomed, false, limitAxes);
+ const LimitPositionResult lp2 = reprap.GetMove().GetKinematics().LimitPosition(ms.coords, ms.initialCoords, numVisibleAxes, effectiveAxesHomed, false, limitAxes);
if (lp2 == LimitPositionResult::ok)
{
- moveState.isCoordinated = false; // change it to an uncoordinated move
+ ms.isCoordinated = false; // change it to an uncoordinated move
break;
}
}
- err = "G0/G1: target position not reachable from current position"; // we can't bring the move within limits, so this is a definite error
- return true;
+ gb.ThrowGCodeException("G0/G1: target position not reachable from current position"); // we can't bring the move within limits, so this is a definite error
case LimitPositionResult::ok:
default:
@@ -1970,53 +2192,52 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
{
AxesBitmap axesMentionedExceptZ = axesMentioned;
axesMentionedExceptZ.ClearBit(Z_AXIS);
- moveState.usePressureAdvance = moveState.hasPositiveExtrusion && axesMentionedExceptZ.IsNonEmpty();
+ ms.usePressureAdvance = ms.hasPositiveExtrusion && axesMentionedExceptZ.IsNonEmpty();
}
// Apply segmentation if necessary. To speed up simulation on SCARA printers, we don't apply kinematics segmentation when simulating.
// As soon as we set segmentsLeft nonzero, the Move process will assume that the move is ready to take, so this must be the last thing we do.
const Kinematics& kin = reprap.GetMove().GetKinematics();
const SegmentationType st = kin.GetSegmentationType();
- if (st.useSegmentation && simulationMode != SimulationMode::normal && (moveState.hasPositiveExtrusion || moveState.isCoordinated || st.useG0Segmentation))
+ if (st.useSegmentation && simulationMode != SimulationMode::normal && (ms.hasPositiveExtrusion || ms.isCoordinated || st.useG0Segmentation))
{
// This kinematics approximates linear motion by means of segmentation
- float moveLengthSquared = fsquare(moveState.currentUserPosition[X_AXIS] - initialUserPosition[X_AXIS]) + fsquare(moveState.currentUserPosition[Y_AXIS] - initialUserPosition[Y_AXIS]);
+ float moveLengthSquared = fsquare(ms.currentUserPosition[X_AXIS] - initialUserPosition[X_AXIS]) + fsquare(ms.currentUserPosition[Y_AXIS] - initialUserPosition[Y_AXIS]);
if (st.useZSegmentation)
{
- moveLengthSquared += fsquare(moveState.currentUserPosition[Z_AXIS] - initialUserPosition[Z_AXIS]);
+ moveLengthSquared += fsquare(ms.currentUserPosition[Z_AXIS] - initialUserPosition[Z_AXIS]);
}
const float moveLength = fastSqrtf(moveLengthSquared);
- const float moveTime = moveLength/(moveState.feedRate * StepClockRate); // this is a best-case time, often the move will take longer
- moveState.totalSegments = (unsigned int)max<long>(1, lrintf(min<float>(moveLength * kin.GetReciprocalMinSegmentLength(), moveTime * kin.GetSegmentsPerSecond())));
+ const float moveTime = moveLength/(ms.feedRate * StepClockRate); // this is a best-case time, often the move will take longer
+ ms.totalSegments = (unsigned int)max<long>(1, lrintf(min<float>(moveLength * kin.GetReciprocalMinSegmentLength(), moveTime * kin.GetSegmentsPerSecond())));
}
else
{
- moveState.totalSegments = 1;
+ ms.totalSegments = 1;
}
- if (reprap.GetMove().IsUsingMesh() && (moveState.isCoordinated || machineType == MachineType::fff))
+ if (reprap.GetMove().IsUsingMesh() && (ms.isCoordinated || machineType == MachineType::fff))
{
const HeightMap& heightMap = reprap.GetMove().AccessHeightMap();
const GridDefinition& grid = heightMap.GetGrid();
const unsigned int minMeshSegments = max<unsigned int>(
1,
heightMap.GetMinimumSegments(
- moveState.currentUserPosition[grid.GetAxisNumber(0)] - initialUserPosition[grid.GetAxisNumber(0)],
- moveState.currentUserPosition[grid.GetAxisNumber(1)] - initialUserPosition[grid.GetAxisNumber(1)]
+ ms.currentUserPosition[grid.GetAxisNumber(0)] - initialUserPosition[grid.GetAxisNumber(0)],
+ ms.currentUserPosition[grid.GetAxisNumber(1)] - initialUserPosition[grid.GetAxisNumber(1)]
)
);
- if (minMeshSegments > moveState.totalSegments)
+ if (minMeshSegments > ms.totalSegments)
{
- moveState.totalSegments = minMeshSegments;
+ ms.totalSegments = minMeshSegments;
}
}
}
- moveState.doingArcMove = false;
- moveState.linearAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetLinearAxes());
- moveState.rotationalAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetRotationalAxes());
- FinaliseMove(gb);
+ ms.doingArcMove = false;
+ ms.linearAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetLinearAxes());
+ ms.rotationalAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetRotationalAxes());
+ FinaliseMove(gb, ms);
UnlockAll(gb); // allow pause
- err = nullptr;
return true;
}
@@ -2025,22 +2246,23 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
// Currently, we do not process new babystepping when executing an arc move
// Return true if finished, false if needs to be called again
// If an error occurs, return true with 'err' assigned
-bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
+bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
{
- // The plans are XY, ZX and YZ depending on the G17/G18/G19 setting. We must use ZX instead of XZ to get the correct arc direction.
+ // The planes are XY, ZX and YZ depending on the G17/G18/G19 setting. We must use ZX instead of XZ to get the correct arc direction.
const unsigned int selectedPlane = gb.LatestMachineState().selectedPlane;
const unsigned int axis0 = (unsigned int[]){ X_AXIS, Z_AXIS, Y_AXIS }[selectedPlane];
const unsigned int axis1 = (axis0 + 1) % 3;
- if (moveFractionToSkip > 0.0)
+ MovementState& ms = GetMovementState(gb);
+ if (ms.moveFractionToSkip > 0.0)
{
- moveState.initialUserC0 = restartInitialUserC0;
- moveState.initialUserC1 = restartInitialUserC1;
+ ms.initialUserC0 = ms.restartInitialUserC0;
+ ms.initialUserC1 = ms.restartInitialUserC1;
}
else
{
- moveState.initialUserC0 = moveState.currentUserPosition[axis0];
- moveState.initialUserC1 = moveState.currentUserPosition[axis1];
+ ms.initialUserC0 = ms.currentUserPosition[axis0];
+ ms.initialUserC1 = ms.currentUserPosition[axis1];
}
// Get the axis parameters
@@ -2050,20 +2272,20 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
newAxisPos[0] = gb.GetDistance();
if (gb.LatestMachineState().axesRelative)
{
- newAxisPos[0] += moveState.initialUserC0;
+ newAxisPos[0] += ms.initialUserC0;
}
else if (gb.LatestMachineState().g53Active)
{
- newAxisPos[0] += GetCurrentToolOffset(axis0);
+ newAxisPos[0] += ms.GetCurrentToolOffset(axis0);
}
else if (!gb.LatestMachineState().runningSystemMacro)
{
- newAxisPos[0] += GetWorkplaceOffset(axis0);
+ newAxisPos[0] += GetWorkplaceOffset(gb, axis0);
}
}
else
{
- newAxisPos[0] = moveState.initialUserC0;
+ newAxisPos[0] = ms.initialUserC0;
}
if (gb.Seen(axisLetters[axis1]))
@@ -2071,20 +2293,20 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
newAxisPos[1] = gb.GetDistance();
if (gb.LatestMachineState().axesRelative)
{
- newAxisPos[1] += moveState.initialUserC1;
+ newAxisPos[1] += ms.initialUserC1;
}
else if (gb.LatestMachineState().g53Active)
{
- newAxisPos[1] += GetCurrentToolOffset(axis1);
+ newAxisPos[1] += ms.GetCurrentToolOffset(axis1);
}
else if (!gb.LatestMachineState().runningSystemMacro)
{
- newAxisPos[1] += GetWorkplaceOffset(axis1);
+ newAxisPos[1] += GetWorkplaceOffset(gb, axis1);
}
}
else
{
- newAxisPos[1] = moveState.initialUserC1;
+ newAxisPos[1] = ms.initialUserC1;
}
float iParam, jParam;
@@ -2094,16 +2316,15 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
const float rParam = gb.GetDistance();
// Get the XY coordinates of the midpoints between the start and end points X and Y distances between start and end points
- const float deltaAxis0 = newAxisPos[0] - moveState.initialUserC0;
- const float deltaAxis1 = newAxisPos[1] - moveState.initialUserC1;
+ const float deltaAxis0 = newAxisPos[0] - ms.initialUserC0;
+ const float deltaAxis1 = newAxisPos[1] - ms.initialUserC1;
const float dSquared = fsquare(deltaAxis0) + fsquare(deltaAxis1); // square of the distance between start and end points
// The distance between start and end points must not be zero
if (dSquared == 0.0)
{
- err = "G2/G3: distance between start and end points must not be zero when specifying a radius";
- return true;
+ gb.ThrowGCodeException("G2/G3: distance between start and end points must not be zero when specifying a radius");
}
// The perpendicular must have a real length (possibly zero)
@@ -2119,8 +2340,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
{
if (hSquared < -0.02 * fsquare(rParam)) // allow the radius to be up to 1% too short
{
- err = "G2/G3: radius is too small to reach endpoint";
- return true;
+ gb.ThrowGCodeException("G2/G3: radius is too small to reach endpoint");
}
hDivD = 0.0; // this has the effect of increasing the radius slightly so that the maths works
}
@@ -2157,23 +2377,22 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
if (iParam == 0.0 && jParam == 0.0) // at least one of IJK must be specified and nonzero
{
- err = "G2/G3: no I J K or R parameter";
- return true;
+ gb.ThrowGCodeException("G2/G3: no I J K or R parameter");
}
}
- memcpyf(moveState.initialCoords, moveState.coords, numVisibleAxes);
+ memcpyf(ms.initialCoords, ms.coords, numVisibleAxes);
// Save the arc centre user coordinates for later
- float userArcCentre[2] = { moveState.initialUserC0 + iParam, moveState.initialUserC1 + jParam };
+ float userArcCentre[2] = { ms.initialUserC0 + iParam, ms.initialUserC1 + jParam };
// Set the new user position
- moveState.currentUserPosition[axis0] = newAxisPos[0];
- moveState.currentUserPosition[axis1] = newAxisPos[1];
+ ms.currentUserPosition[axis0] = newAxisPos[0];
+ ms.currentUserPosition[axis1] = newAxisPos[1];
// CNC machines usually do a full circle if the initial and final XY coordinates are the same.
// Usually this is because X and Y were not given, but repeating the coordinates is permitted.
- const bool wholeCircle = (moveState.initialUserC0 == moveState.currentUserPosition[axis0] && moveState.initialUserC1 == moveState.currentUserPosition[axis1]);
+ const bool wholeCircle = (ms.initialUserC0 == ms.currentUserPosition[axis0] && ms.initialUserC1 == ms.currentUserPosition[axis1]);
// Get any additional axes
AxesBitmap axesMentioned;
@@ -2186,122 +2405,147 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
const float moveArg = gb.GetDistance();
if (gb.LatestMachineState().axesRelative)
{
- moveState.currentUserPosition[axis] += moveArg * (1.0 - moveFractionToSkip);
+ ms.currentUserPosition[axis] += moveArg * (1.0 - ms.moveFractionToSkip);
}
else if (gb.LatestMachineState().g53Active)
{
- moveState.currentUserPosition[axis] = moveArg + GetCurrentToolOffset(axis); // g53 ignores tool offsets as well as workplace coordinates
+ ms.currentUserPosition[axis] = moveArg + ms.GetCurrentToolOffset(axis); // g53 ignores tool offsets as well as workplace coordinates
}
else if (gb.LatestMachineState().runningSystemMacro)
{
- moveState.currentUserPosition[axis] = moveArg; // don't apply workplace offsets to commands in system macros
+ ms.currentUserPosition[axis] = moveArg; // don't apply workplace offsets to commands in system macros
}
else
{
- moveState.currentUserPosition[axis] = moveArg + GetWorkplaceOffset(axis);
+ ms.currentUserPosition[axis] = moveArg + GetWorkplaceOffset(gb, axis);
}
axesMentioned.SetBit(axis);
}
}
// Check enough axes have been homed
- if (CheckEnoughAxesHomed(axesMentioned))
+ AxesBitmap realAxesMoving;
+ if (ms.currentTool == nullptr)
{
- err = "G2/G3: insufficient axes homed";
- return true;
+ realAxesMoving = axesMentioned;
+ }
+ else
+ {
+ realAxesMoving = axesMentioned & ~AxesBitmap::MakeFromBits(X_AXIS, Y_AXIS);
+ if (axesMentioned.IsBitSet(X_AXIS))
+ {
+ realAxesMoving |= ms.currentTool->GetXAxisMap();
+ }
+ if (axesMentioned.IsBitSet(Y_AXIS))
+ {
+ realAxesMoving |= ms.currentTool->GetYAxisMap();
+ }
}
+ if (CheckEnoughAxesHomed(realAxesMoving))
+ {
+ gb.ThrowGCodeException("G2/G3: insufficient axes homed");
+ }
+
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, realAxesMoving);
+#endif
+
// Compute the initial and final angles. Do this before we possible rotate the coordinates of the arc centre.
- float finalTheta = atan2(moveState.currentUserPosition[axis1] - userArcCentre[1], moveState.currentUserPosition[axis0] - userArcCentre[0]);
- moveState.arcRadius = fastSqrtf(iParam * iParam + jParam * jParam);
- moveState.arcCurrentAngle = atan2(-jParam, -iParam);
+ float finalTheta = atan2(ms.currentUserPosition[axis1] - userArcCentre[1], ms.currentUserPosition[axis0] - userArcCentre[0]);
+ ms.arcRadius = fastSqrtf(iParam * iParam + jParam * jParam);
+ ms.arcCurrentAngle = atan2(-jParam, -iParam);
// Transform to machine coordinates and check that it is within limits
+
#if SUPPORT_COORDINATE_ROTATION
// Apply coordinate rotation to the final and the centre coordinates
if (g68Angle != 0.0 && gb.DoingCoordinateRotation())
{
+ const AxesBitmap xAndY = AxesBitmap::MakeFromBits(X_AXIS, Y_AXIS);
+ if (axesMentioned.Intersects(xAndY))
+ {
+ axesMentioned |= xAndY; // if either X or Y is moving and we are rotating coordinates, both are moving
+ }
float coords[MaxAxes];
- memcpyf(coords, moveState.currentUserPosition, MaxAxes);
+ memcpyf(coords, ms.currentUserPosition, MaxAxes);
RotateCoordinates(g68Angle, coords);
- ToolOffsetTransform(coords, moveState.coords, axesMentioned); // set the final position
+ ToolOffsetTransform(ms, coords, ms.coords, axesMentioned); // set the final position
RotateCoordinates(g68Angle, userArcCentre);
finalTheta -= g68Angle * DegreesToRadians;
- moveState.arcCurrentAngle -= g68Angle * DegreesToRadians;
+ ms.arcCurrentAngle -= g68Angle * DegreesToRadians;
}
else
#endif
{
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords, axesMentioned); // set the final position
+ ToolOffsetTransform(ms, axesMentioned); // set the final position
}
- if (reprap.GetMove().GetKinematics().LimitPosition(moveState.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, true, limitAxes) != LimitPositionResult::ok)
+#if SUPPORT_ASYNC_MOVES
+ // Check the final position for collisions. We check the intermediate positions as we go.
+ collisionChecker.UpdatePositions(ms.coords);
+#endif
+
+ if (reprap.GetMove().GetKinematics().LimitPosition(ms.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, true, limitAxes) != LimitPositionResult::ok)
{
- err = "G2/G3: outside machine limits"; // abandon the move
- return true;
+ gb.ThrowGCodeException("G2/G3: outside machine limits"); // abandon the move
}
// Set up default move parameters
- moveState.checkEndstops = false;
- moveState.reduceAcceleration = false;
- moveState.moveType = 0;
- moveState.tool = reprap.GetCurrentTool();
- moveState.isCoordinated = true;
+ ms.checkEndstops = false;
+ ms.reduceAcceleration = false;
+ ms.moveType = 0;
+ ms.movementTool = ms.currentTool;
+ ms.isCoordinated = true;
// Set up the arc centre coordinates and record which axes behave like an X axis.
// The I and J parameters are always relative to present position.
// For X and Y we need to set up the arc centre for each axis that X or Y is mapped to.
- const AxesBitmap axis0Mapping = reprap.GetCurrentAxisMapping(axis0);
- const AxesBitmap axis1Mapping = reprap.GetCurrentAxisMapping(axis1);
+ const AxesBitmap axis0Mapping = ms.GetCurrentAxisMapping(axis0);
+ const AxesBitmap axis1Mapping = ms.GetCurrentAxisMapping(axis1);
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
if (axis0Mapping.IsBitSet(axis))
{
- moveState.arcCentre[axis] = (userArcCentre[0] * axisScaleFactors[axis]) + currentBabyStepOffsets[axis] - Tool::GetOffset(reprap.GetCurrentTool(), axis);
+ ms.arcCentre[axis] = (userArcCentre[0] * axisScaleFactors[axis]) + currentBabyStepOffsets[axis] - Tool::GetOffset(ms.currentTool, axis);
}
else if (axis1Mapping.IsBitSet(axis))
{
- moveState.arcCentre[axis] = (userArcCentre[1] * axisScaleFactors[axis]) + currentBabyStepOffsets[axis] - Tool::GetOffset(reprap.GetCurrentTool(), axis);
+ ms.arcCentre[axis] = (userArcCentre[1] * axisScaleFactors[axis]) + currentBabyStepOffsets[axis] - Tool::GetOffset(ms.currentTool, axis);
}
}
- err = LoadExtrusionAndFeedrateFromGCode(gb, true);
- if (err != nullptr)
- {
- return true;
- }
+ LoadExtrusionAndFeedrateFromGCode(gb, ms, true);
- if (buildObjects.IsFirstMoveSincePrintingResumed())
+ if (ms.IsFirstMoveSincePrintingResumed())
{
- if (moveState.hasPositiveExtrusion) // check whether this is the first move after skipping an object and is extruding
+ if (ms.hasPositiveExtrusion) // check whether this is the first move after skipping an object and is extruding
{
if (TravelToStartPoint(gb)) // don't start a printing move from the wrong point
{
- buildObjects.DoneMoveSincePrintingResumed();
+ ms.DoneMoveSincePrintingResumed();
}
return false;
}
else
{
- buildObjects.DoneMoveSincePrintingResumed();
+ ms.DoneMoveSincePrintingResumed();
}
}
-#if TRACK_OBJECT_NAMES
- if (moveState.hasPositiveExtrusion)
+ if (ms.hasPositiveExtrusion)
{
//TODO ideally we should calculate the min and max X and Y coordinates of the entire arc here and call UpdateObjectCoordinates twice.
// But it is currently very rare to use G2/G3 with extrusion, so for now we don't bother.
- buildObjects.UpdateObjectCoordinates(moveState.currentUserPosition, AxesBitmap::MakeLowestNBits(2));
+ buildObjects.UpdateObjectCoordinates(ms.currentObjectNumber, ms.currentUserPosition, AxesBitmap::MakeLowestNBits(2));
}
-#endif
#if SUPPORT_LASER
if (machineType == MachineType::laser)
{
if (gb.Seen('S'))
{
- moveState.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
+ ms.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
}
else if (laserPowerSticky)
{
@@ -2309,7 +2553,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
}
else
{
- moveState.laserPwmOrIoBits.laserPwm = 0;
+ ms.laserPwmOrIoBits.laserPwm = 0;
}
}
# if SUPPORT_IOBITS
@@ -2321,7 +2565,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
// Update the iobits parameter
if (gb.Seen('P'))
{
- moveState.laserPwmOrIoBits.ioBits = (IoBits_t)gb.GetIValue();
+ ms.laserPwmOrIoBits.ioBits = (IoBits_t)gb.GetIValue();
}
else
{
@@ -2330,7 +2574,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
}
#endif
- moveState.usePressureAdvance = moveState.hasPositiveExtrusion;
+ ms.usePressureAdvance = ms.hasPositiveExtrusion;
// Calculate the total angle moved, which depends on which way round we are going
float totalArc;
@@ -2340,7 +2584,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
}
else
{
- totalArc = (clockwise) ? moveState.arcCurrentAngle - finalTheta : finalTheta - moveState.arcCurrentAngle;
+ totalArc = (clockwise) ? ms.arcCurrentAngle - finalTheta : finalTheta - ms.arcCurrentAngle;
if (totalArc < 0.0)
{
totalArc += TwoPi;
@@ -2352,27 +2596,27 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
// We leave out the square term because it is very small
// In CNC applications even very small deviations can be visible, so we use a smaller segment length at low speeds
const float arcSegmentLength = constrain<float>
- ( min<float>(fastSqrtf(8 * moveState.arcRadius * MaxArcDeviation), moveState.feedRate * StepClockRate * (1.0/MinArcSegmentsPerSec)),
+ ( min<float>(fastSqrtf(8 * ms.arcRadius * MaxArcDeviation), ms.feedRate * StepClockRate * (1.0/MinArcSegmentsPerSec)),
MinArcSegmentLength,
MaxArcSegmentLength
);
- moveState.totalSegments = max<unsigned int>((unsigned int)((moveState.arcRadius * totalArc)/arcSegmentLength + 0.8), 1u);
- moveState.arcAngleIncrement = totalArc/moveState.totalSegments;
+ ms.totalSegments = max<unsigned int>((unsigned int)((ms.arcRadius * totalArc)/arcSegmentLength + 0.8), 1u);
+ ms.arcAngleIncrement = totalArc/ms.totalSegments;
if (clockwise)
{
- moveState.arcAngleIncrement = -moveState.arcAngleIncrement;
+ ms.arcAngleIncrement = -ms.arcAngleIncrement;
}
- moveState.angleIncrementSine = sinf(moveState.arcAngleIncrement);
- moveState.angleIncrementCosine = cosf(moveState.arcAngleIncrement);
- moveState.segmentsTillNextFullCalc = 0;
+ ms.angleIncrementSine = sinf(ms.arcAngleIncrement);
+ ms.angleIncrementCosine = cosf(ms.arcAngleIncrement);
+ ms.segmentsTillNextFullCalc = 0;
- moveState.arcAxis0 = axis0;
- moveState.arcAxis1 = axis1;
- moveState.doingArcMove = true;
- moveState.xyPlane = (selectedPlane == 0);
- moveState.linearAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetLinearAxes());
- moveState.rotationalAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetRotationalAxes());
- FinaliseMove(gb);
+ ms.arcAxis0 = axis0;
+ ms.arcAxis1 = axis1;
+ ms.doingArcMove = true;
+ ms.xyPlane = (selectedPlane == 0);
+ ms.linearAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetLinearAxes());
+ ms.rotationalAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetRotationalAxes());
+ FinaliseMove(gb, ms);
UnlockAll(gb); // allow pause
// debugPrintf("Radius %.2f, initial angle %.1f, increment %.1f, segments %u\n",
// arcRadius, arcCurrentAngle * RadiansToDegrees, arcAngleIncrement * RadiansToDegrees, segmentsLeft);
@@ -2380,13 +2624,13 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
}
// Adjust the move parameters to account for segmentation and/or part of the move having been done already
-void GCodes::FinaliseMove(GCodeBuffer& gb) noexcept
+void GCodes::FinaliseMove(GCodeBuffer& gb, MovementState& ms) noexcept
{
- moveState.canPauseAfter = !moveState.checkEndstops && !moveState.doingArcMove; // pausing during an arc move isn't safe because the arc centre get recomputed incorrectly when we resume
- moveState.filePos = (&gb == fileGCode) ? gb.GetFilePosition() : noFilePosition;
+ ms.canPauseAfter = !ms.checkEndstops && !ms.doingArcMove; // pausing during an arc move isn't safe because the arc centre get recomputed incorrectly when we resume
+ ms.filePos = gb.GetJobFilePosition();
gb.MotionCommanded();
- if (buildObjects.IsCurrentObjectCancelled())
+ if (ms.IsCurrentObjectCancelled())
{
#if SUPPORT_LASER
if (machineType == MachineType::laser)
@@ -2397,34 +2641,34 @@ void GCodes::FinaliseMove(GCodeBuffer& gb) noexcept
}
else
{
- if (moveState.totalSegments > 1)
+ if (ms.totalSegments > 1)
{
- moveState.segMoveState = SegmentedMoveState::active;
+ ms.segMoveState = SegmentedMoveState::active;
gb.SetState(GCodeState::waitingForSegmentedMoveToGo);
for (size_t extruder = 0; extruder < numExtruders; ++extruder)
{
- moveState.coords[ExtruderToLogicalDrive(extruder)] /= moveState.totalSegments; // change the extrusion to extrusion per segment
+ ms.coords[ExtruderToLogicalDrive(extruder)] /= ms.totalSegments; // change the extrusion to extrusion per segment
}
- if (moveFractionToSkip != 0.0)
+ if (ms.moveFractionToSkip != 0.0)
{
- const float fseg = floor(moveState.totalSegments * moveFractionToSkip); // round down to the start of a move
- segmentsLeftToStartAt = moveState.totalSegments - (unsigned int)fseg;
- firstSegmentFractionToSkip = (moveFractionToSkip * moveState.totalSegments) - fseg;
- NewMoveAvailable();
+ const float fseg = floor(ms.totalSegments * ms.moveFractionToSkip); // round down to the start of a move
+ ms.segmentsLeftToStartAt = ms.totalSegments - (unsigned int)fseg;
+ ms.firstSegmentFractionToSkip = (ms.moveFractionToSkip * ms.totalSegments) - fseg;
+ NewMoveAvailable(ms);
return;
}
}
else
{
- moveState.segMoveState = SegmentedMoveState::inactive;
+ ms.segMoveState = SegmentedMoveState::inactive;
}
- segmentsLeftToStartAt = moveState.totalSegments;
- firstSegmentFractionToSkip = moveFractionToSkip;
+ ms.segmentsLeftToStartAt = ms.totalSegments;
+ ms.firstSegmentFractionToSkip = ms.moveFractionToSkip;
- NewMoveAvailable();
+ NewMoveAvailable(ms);
}
}
@@ -2438,162 +2682,156 @@ bool GCodes::TravelToStartPoint(GCodeBuffer& gb) noexcept
return false;
}
- SetMoveBufferDefaults();
- ToolOffsetTransform(moveState.currentUserPosition, moveState.initialCoords);
- ToolOffsetTransform(buildObjects.GetInitialPosition().moveCoords, moveState.coords);
- moveState.feedRate = buildObjects.GetInitialPosition().feedRate;
- moveState.tool = reprap.GetCurrentTool();
- moveState.linearAxesMentioned = moveState.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
- NewSingleSegmentMoveAvailable();
+ MovementState& ms = GetMovementState(gb);
+ ms.SetDefaults(numTotalAxes);
+ SetMoveBufferDefaults(ms);
+ ToolOffsetTransform(ms);
+ const RestorePoint& rp = ms.restorePoints[ResumeObjectRestorePointNumber];
+ ToolOffsetTransform(ms, rp.moveCoords, ms.coords);
+ ms.feedRate = rp.feedRate;
+ ms.movementTool = ms.currentTool;
+ ms.linearAxesMentioned = ms.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
+ NewSingleSegmentMoveAvailable(ms);
return true;
}
// The Move class calls this function to find what to do next. It takes its own copy of the move because it adjusts the coordinates.
// Returns true if a new move was copied to 'm'.
-bool GCodes::ReadMove(RawMove& m) noexcept
+bool GCodes::ReadMove(unsigned int queueNumber, RawMove& m) noexcept
{
- if (moveState.segmentsLeft == 0)
+ MovementState& ms = moveStates[queueNumber];
+ if (ms.segmentsLeft == 0)
{
return false;
}
while (true) // loop while we skip move segments
{
- m = moveState;
+ m = ms;
- if (moveState.segmentsLeft == 1)
+ if (ms.segmentsLeft == 1)
{
// If there is just 1 segment left, it doesn't matter if it is an arc move or not, just move to the end position
- if (segmentsLeftToStartAt == 1 && firstSegmentFractionToSkip != 0.0) // if this is the segment we are starting at and we need to skip some of it
+ if (ms.segmentsLeftToStartAt == 1 && ms.firstSegmentFractionToSkip != 0.0) // if this is the segment we are starting at and we need to skip some of it
{
// Reduce the extrusion by the amount to be skipped
for (size_t extruder = 0; extruder < numExtruders; ++extruder)
{
- m.coords[ExtruderToLogicalDrive(extruder)] *= (1.0 - firstSegmentFractionToSkip);
+ m.coords[ExtruderToLogicalDrive(extruder)] *= (1.0 - ms.firstSegmentFractionToSkip);
}
}
m.proportionDone = 1.0;
- if (moveState.doingArcMove)
+ if (ms.doingArcMove)
{
m.canPauseAfter = true; // we can pause after the final segment of an arc move
}
- ClearMove();
+ ms.ClearMove();
}
else
{
// This move needs to be divided into 2 or more segments
// Do the axes
AxesBitmap axisMap0, axisMap1;
- if (moveState.doingArcMove)
+ if (ms.doingArcMove)
{
- moveState.arcCurrentAngle += moveState.arcAngleIncrement;
- if (moveState.segmentsTillNextFullCalc == 0)
+ ms.arcCurrentAngle += ms.arcAngleIncrement;
+ if (ms.segmentsTillNextFullCalc == 0)
{
// Do the full calculation
- moveState.segmentsTillNextFullCalc = SegmentsPerFulArcCalculation;
- moveState.currentAngleCosine = cosf(moveState.arcCurrentAngle);
- moveState.currentAngleSine = sinf(moveState.arcCurrentAngle);
+ ms.segmentsTillNextFullCalc = SegmentsPerFulArcCalculation;
+ ms.currentAngleCosine = cosf(ms.arcCurrentAngle);
+ ms.currentAngleSine = sinf(ms.arcCurrentAngle);
}
else
{
// Speed up the computation by doing two multiplications and an addition or subtraction instead of a sine or cosine
- --moveState.segmentsTillNextFullCalc;
- const float newCosine = moveState.currentAngleCosine * moveState.angleIncrementCosine - moveState.currentAngleSine * moveState.angleIncrementSine;
- const float newSine = moveState.currentAngleSine * moveState.angleIncrementCosine + moveState.currentAngleCosine * moveState.angleIncrementSine;
- moveState.currentAngleCosine = newCosine;
- moveState.currentAngleSine = newSine;
+ --ms.segmentsTillNextFullCalc;
+ const float newCosine = ms.currentAngleCosine * ms.angleIncrementCosine - ms.currentAngleSine * ms.angleIncrementSine;
+ const float newSine = ms.currentAngleSine * ms.angleIncrementCosine + ms.currentAngleCosine * ms.angleIncrementSine;
+ ms.currentAngleCosine = newCosine;
+ ms.currentAngleSine = newSine;
}
- axisMap0 = Tool::GetAxisMapping(moveState.tool, moveState.arcAxis0);
- axisMap1 = Tool::GetAxisMapping(moveState.tool, moveState.arcAxis1);
- moveState.cosXyAngle = (moveState.xyPlane) ? moveState.angleIncrementCosine : 1.0;
+ axisMap0 = Tool::GetAxisMapping(ms.movementTool, ms.arcAxis0);
+ axisMap1 = Tool::GetAxisMapping(ms.movementTool, ms.arcAxis1);
+ ms.cosXyAngle = (ms.xyPlane) ? ms.angleIncrementCosine : 1.0;
}
for (size_t drive = 0; drive < numVisibleAxes; ++drive)
{
- if (moveState.doingArcMove && axisMap1.IsBitSet(drive))
+ if (ms.doingArcMove && axisMap1.IsBitSet(drive))
{
// Axis1 or a substitute in the selected plane
- moveState.initialCoords[drive] = moveState.arcCentre[drive] + moveState.arcRadius * axisScaleFactors[drive] * moveState.currentAngleSine;
+ ms.initialCoords[drive] = ms.arcCentre[drive] + ms.arcRadius * axisScaleFactors[drive] * ms.currentAngleSine;
}
- else if (moveState.doingArcMove && axisMap0.IsBitSet(drive))
+ else if (ms.doingArcMove && axisMap0.IsBitSet(drive))
{
// Axis0 or a substitute in the selected plane
- moveState.initialCoords[drive] = moveState.arcCentre[drive] + moveState.arcRadius * axisScaleFactors[drive] * moveState.currentAngleCosine;
+ ms.initialCoords[drive] = ms.arcCentre[drive] + ms.arcRadius * axisScaleFactors[drive] * ms.currentAngleCosine;
}
else
{
// This axis is not moving in an arc
- const float movementToDo = (moveState.coords[drive] - moveState.initialCoords[drive])/moveState.segmentsLeft;
- moveState.initialCoords[drive] += movementToDo;
+ const float movementToDo = (ms.coords[drive] - ms.initialCoords[drive])/ms.segmentsLeft;
+ ms.initialCoords[drive] += movementToDo;
}
- m.coords[drive] = moveState.initialCoords[drive];
+ m.coords[drive] = ms.initialCoords[drive];
}
- if (segmentsLeftToStartAt < moveState.segmentsLeft)
+ if (ms.segmentsLeftToStartAt < ms.segmentsLeft)
{
// We are resuming a print part way through a move and we printed this segment already
- --moveState.segmentsLeft;
+ --ms.segmentsLeft;
continue;
}
// Limit the end position at each segment. This is needed for arc moves on any printer, and for [segmented] straight moves on SCARA printers.
- if (reprap.GetMove().GetKinematics().LimitPosition(m.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, true, limitAxes) != LimitPositionResult::ok)
+ if ( reprap.GetMove().GetKinematics().LimitPosition(m.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, true, limitAxes) != LimitPositionResult::ok
+#if SUPPORT_ASYNC_MOVES
+ || !collisionChecker.UpdatePositions(m.coords)
+#endif
+ )
{
- moveState.segMoveState = SegmentedMoveState::aborted;
- moveState.doingArcMove = false;
- moveState.segmentsLeft = 0;
+ ms.segMoveState = SegmentedMoveState::aborted;
+ ms.doingArcMove = false;
+ ms.segmentsLeft = 0;
return false;
}
- if (segmentsLeftToStartAt == moveState.segmentsLeft && firstSegmentFractionToSkip != 0.0) // if this is the segment we are starting at and we need to skip some of it
+ if (ms.segmentsLeftToStartAt == ms.segmentsLeft && ms.firstSegmentFractionToSkip != 0.0) // if this is the segment we are starting at and we need to skip some of it
{
// Reduce the extrusion by the amount to be skipped
for (size_t extruder = 0; extruder < numExtruders; ++extruder)
{
- m.coords[ExtruderToLogicalDrive(extruder)] *= (1.0 - firstSegmentFractionToSkip);
+ m.coords[ExtruderToLogicalDrive(extruder)] *= (1.0 - ms.firstSegmentFractionToSkip);
}
}
- --moveState.segmentsLeft;
+ --ms.segmentsLeft;
- m.proportionDone = moveState.GetProportionDone();
+ m.proportionDone = ms.GetProportionDone();
}
return true;
}
}
-void GCodes::ClearMove() noexcept
-{
- TaskCriticalSectionLocker lock; // make sure that other tasks sees a consistent memory state
-
- moveState.segmentsLeft = 0;
- moveState.segMoveState = SegmentedMoveState::inactive;
- moveState.doingArcMove = false;
- moveState.checkEndstops = false;
- moveState.reduceAcceleration = false;
- moveState.moveType = 0;
- moveState.applyM220M221 = false;
- moveFractionToSkip = 0.0;
-}
-
-// Flag that a new single-segment move is available for consumption by the Move subsystem
+// Flag that a new move is available for consumption by the Move subsystem
// Code that sets up a new move should ensure that segmentsLeft is zero, then set up all the move parameters,
// then call this function to update SegmentsLeft safely in a multi-threaded environment
-void GCodes::NewSingleSegmentMoveAvailable() noexcept
+void GCodes::NewSingleSegmentMoveAvailable(MovementState& ms) noexcept
{
- moveState.totalSegments = 1;
+ ms.totalSegments = 1;
__DMB(); // make sure that all the move details have been written first
- moveState.segmentsLeft = 1; // set the number of segments to indicate that a move is available to be taken
+ ms.segmentsLeft = 1; // set the number of segments to indicate that a move is available to be taken
reprap.GetMove().MoveAvailable(); // notify the Move task that we have a move
}
// Flag that a new move is available for consumption by the Move subsystem
// This version is for when totalSegments has already be set up.
-void GCodes::NewMoveAvailable() noexcept
+void GCodes::NewMoveAvailable(MovementState& ms) noexcept
{
- const unsigned int sl = moveState.totalSegments;
+ const unsigned int sl = ms.totalSegments;
__DMB(); // make sure that the move details have been written first
- moveState.segmentsLeft = sl; // set the number of segments to indicate that a move is available to be taken
+ ms.segmentsLeft = sl; // set the number of segments to indicate that a move is available to be taken
reprap.GetMove().MoveAvailable(); // notify the Move task that we have a move
}
@@ -2601,7 +2839,7 @@ void GCodes::NewMoveAvailable() noexcept
void GCodes::AbortPrint(GCodeBuffer& gb) noexcept
{
(void)gb.AbortFile(true); // stop executing any files or macros that this GCodeBuffer is running
- if (&gb == fileGCode) // if the current command came from a file being printed
+ if (gb.IsFileChannel()) // if the current command came from a file being printed
{
StopPrint(StopPrintReason::abort);
}
@@ -2618,7 +2856,10 @@ void GCodes::EmergencyStop() noexcept
}
}
#if SUPPORT_LASER
- moveState.laserPwmOrIoBits.laserPwm = 0;
+ for (MovementState& ms : moveStates)
+ {
+ ms.laserPwmOrIoBits.laserPwm = 0;
+ }
#endif
}
@@ -2642,10 +2883,12 @@ bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissi
// otherwise it is either the G- or M-code being executed, or ToolChangeMacroCode for a tool change file, or SystemMacroCode for another system file
bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, int codeRunning, VariableSet& initialVariables) noexcept
{
- if (codeRunning != AsyncSystemMacroCode && &gb == fileGCode && gb.LatestMachineState().GetPrevious() == nullptr)
+ if ( codeRunning != AsyncSystemMacroCode
+ && gb.IsFileChannel()
+ && gb.LatestMachineState().GetPrevious() == nullptr)
{
// This macro was invoked directly from the print file by M98, G28, G29, G32 etc. so record the file location of that command so that we can restart it
- printFilePositionAtMacroStart = gb.GetFilePosition();
+ gb.SavePrintingFilePosition();
}
#if HAS_SBC_INTERFACE
@@ -2755,7 +2998,7 @@ bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissi
// Return true if the macro being executed by fileGCode was restarted
bool GCodes::GetMacroRestarted() const noexcept
{
- const GCodeMachineState& ms = fileGCode->LatestMachineState();
+ const GCodeMachineState& ms = FileGCode()->LatestMachineState();
return ms.doingFileMacro && ms.GetPrevious() != nullptr && ms.GetPrevious()->firstCommandAfterRestart;
}
@@ -2784,31 +3027,10 @@ void GCodes::FileMacroCyclesReturn(GCodeBuffer& gb) noexcept
}
}
-// Home one or more of the axes
+// Home one or more of the axes. Taking the movement lock and syncing has already been done.
// 'reply' is only written if there is an error.
GCodeResult GCodes::DoHome(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return GCodeResult::notFinished;
- }
-
-#if SUPPORT_ROLAND
- // Deal with a Roland configuration
- if (reprap.GetRoland()->Active())
- {
- bool rolHome = reprap.GetRoland()->ProcessHome();
- if (rolHome)
- {
- for(size_t axis = 0; axis < AXES; axis++)
- {
- axisIsHomed[axis] = true;
- }
- }
- return rolHome;
- }
-#endif
-
// We have the movement lock so we have exclusive access to the homing flags
if (toBeHomed.IsNonEmpty())
{
@@ -2836,261 +3058,27 @@ GCodeResult GCodes::DoHome(GCodeBuffer& gb, const StringRef& reply) THROWS(GCode
return GCodeResult::ok;
}
-// This is called to execute a G30.
-// It sets wherever we are as the probe point P (probePointIndex) then probes the bed, or gets all its parameters from the arguments.
-// If X or Y are specified, use those; otherwise use the machine's coordinates. If no Z is specified use the machine's coordinates.
-// If it is specified and is greater than SILLY_Z_VALUE (i.e. greater than -9999.0) then that value is used.
-// If it's less than SILLY_Z_VALUE the bed is probed and that value is used.
-// We already own the movement lock before this is called.
-GCodeResult GCodes::ExecuteG30(GCodeBuffer& gb, const StringRef& reply)
-{
- g30SValue = (gb.Seen('S')) ? gb.GetIValue() : -4; // S-4 or lower is equivalent to having no S parameter
- if (g30SValue == -2 && reprap.GetCurrentTool() == nullptr)
- {
- reply.copy("G30 S-2 commanded with no tool selected");
- return GCodeResult::error;
- }
-
- g30HValue = (gb.Seen('H')) ? gb.GetFValue() : 0.0;
- g30ProbePointIndex = -1;
- bool seenP = false;
- gb.TryGetIValue('P', g30ProbePointIndex, seenP);
- if (seenP)
- {
- if (g30ProbePointIndex < 0 || g30ProbePointIndex >= (int)MaxProbePoints)
- {
- reply.copy("Z probe point index out of range");
- return GCodeResult::error;
- }
- else
- {
- // Set the specified probe point index to the specified coordinates
- const float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : moveState.currentUserPosition[X_AXIS];
- const float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : moveState.currentUserPosition[Y_AXIS];
- const float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : moveState.currentUserPosition[Z_AXIS];
- reprap.GetMove().SetXYBedProbePoint((size_t)g30ProbePointIndex, x, y);
-
- if (z > SILLY_Z_VALUE)
- {
- // Just set the height error to the specified Z coordinate
- reprap.GetMove().SetZBedProbePoint((size_t)g30ProbePointIndex, z, false, false);
- if (g30SValue >= -1)
- {
- return GetGCodeResultFromError(reprap.GetMove().FinishedBedProbing(g30SValue, reply));
- }
- }
- else
- {
- // Do a Z probe at the specified point.
- const auto zp = SetZProbeNumber(gb, 'K'); // may throw, so do this before changing the state
- gb.SetState(GCodeState::probingAtPoint0);
- if (zp->GetProbeType() != ZProbeType::blTouch)
- {
- DeployZProbe(gb);
- }
- }
- }
- }
- else
- {
- // G30 without P parameter. This probes the current location starting from the current position.
- // If S=-1 it just reports the stopped height, else it resets the Z origin.
- const auto zp = SetZProbeNumber(gb, 'K'); // may throw, so do this before changing the state
- InitialiseTaps(zp->HasTwoProbingSpeeds());
- gb.SetState(GCodeState::probingAtPoint2a);
- if (zp->GetProbeType() != ZProbeType::blTouch)
- {
- DeployZProbe(gb);
- }
- }
- return GCodeResult::ok;
-}
-
-// Set up currentZProbeNumber and return the probe
-ReadLockedPointer<ZProbe> GCodes::SetZProbeNumber(GCodeBuffer& gb, char probeLetter) THROWS(GCodeException)
-{
- const uint32_t probeNumber = (gb.Seen(probeLetter)) ? gb.GetLimitedUIValue(probeLetter, MaxZProbes) : 0;
- auto zp = reprap.GetPlatform().GetEndstops().GetZProbe(probeNumber);
- if (zp.IsNull())
- {
- throw GCodeException(gb.GetLineNumber(), -1, "Z probe %" PRIu32 " not found", probeNumber);
- }
- currentZProbeNumber = (uint8_t)probeNumber;
- return zp;
-}
-
-// Decide which device to display a message box on
-MessageType GCodes::GetMessageBoxDevice(GCodeBuffer& gb) const
-{
- MessageType mt = gb.GetResponseMessageType();
- if (mt == GenericMessage)
- {
- // Command source was the file being printed, or a trigger. Send the message to PanelDue if there is one, else to the web server.
- mt = (lastAuxStatusReportType >= 0) ? AuxMessage : HttpMessage;
- }
- return mt;
-}
-
-void GCodes::DoManualProbe(GCodeBuffer& gb, const char *message, const char *title, const AxesBitmap axes)
-{
- if (Push(gb, true)) // stack the machine state including the file position and set the state to GCodeState::normal
- {
- gb.WaitForAcknowledgement(); // flag that we are waiting for acknowledgement
- const MessageType mt = GetMessageBoxDevice(gb);
- platform.SendAlert(mt, message, title, 2, 0.0, axes);
- }
-}
-
-// Do a manual bed probe. On entry the state variable is the state we want to return to when the user has finished adjusting the height.
-void GCodes::DoManualBedProbe(GCodeBuffer& gb)
-{
- DoManualProbe(gb, "Adjust height until the nozzle just touches the bed, then press OK", "Manual bed probing", AxesBitmap::MakeFromBits(Z_AXIS));
-}
-
-// Start probing the grid, returning true if we didn't because of an error.
-// Prior to calling this the movement system must be locked.
-GCodeResult GCodes::ProbeGrid(GCodeBuffer& gb, const StringRef& reply)
-{
- if (!defaultGrid.IsValid())
- {
- reply.copy("No valid grid defined for bed probing");
- return GCodeResult::error;
- }
-
- if (!AllAxesAreHomed())
- {
- reply.copy("Must home printer before bed probing");
- return GCodeResult::error;
- }
-
- const auto zp = SetZProbeNumber(gb, 'K'); // may throw, so do this before changing the state
-
- reprap.GetMove().AccessHeightMap().SetGrid(defaultGrid);
- ClearBedMapping();
- gridAxis0index = gridAxis1index = 0;
-
- gb.SetState(GCodeState::gridProbing1);
- if (zp->GetProbeType() != ZProbeType::blTouch)
- {
- DeployZProbe(gb);
- }
- return GCodeResult::ok;
-}
-
-#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
-
-GCodeResult GCodes::LoadHeightMap(GCodeBuffer& gb, const StringRef& reply)
-{
- ClearBedMapping();
-
- String<MaxFilenameLength> heightMapFileName;
- bool seen = false;
- gb.TryGetQuotedString('P', heightMapFileName.GetRef(), seen);
- if (!seen)
- {
- heightMapFileName.copy(DefaultHeightMapFile);
- }
-
- String<MaxFilenameLength> fullName;
- platform.MakeSysFileName(fullName.GetRef(), heightMapFileName.c_str());
- FileStore * const f = MassStorage::OpenFile(fullName.c_str(), OpenMode::read, 0);
- if (f == nullptr)
- {
- reply.printf("Height map file %s not found", fullName.c_str());
- return GCodeResult::error;
- }
- reply.printf("Failed to load height map from file %s: ", fullName.c_str()); // set up error message to append to
-
- const bool err = reprap.GetMove().LoadHeightMapFromFile(f, fullName.c_str(), reply);
- f->Close();
-
- ActivateHeightmap(!err);
- if (err)
- {
- return GCodeResult::error;
- }
-
- reply.Clear(); // get rid of the error message
- if (!zDatumSetByProbing && platform.GetZProbeOrDefault(0)->GetProbeType() != ZProbeType::none) //TODO store Z probe number in height map
- {
- reply.copy("the height map was loaded when the current Z=0 datum was not determined by probing. This may result in a height offset.");
- return GCodeResult::warning;
- }
-
- return GCodeResult::ok;
-}
-
-// Save the height map and append the success or error message to 'reply', returning true if an error occurred
-bool GCodes::TrySaveHeightMap(const char *filename, const StringRef& reply) const noexcept
-{
- String<MaxFilenameLength> fullName;
- platform.MakeSysFileName(fullName.GetRef(), filename);
- FileStore * const f = MassStorage::OpenFile(fullName.c_str(), OpenMode::write, 0);
- bool err;
- if (f == nullptr)
- {
- reply.catf("Failed to create height map file %s", fullName.c_str());
- err = true;
- }
- else
- {
- err = reprap.GetMove().SaveHeightMapToFile(f, fullName.c_str());
- f->Close();
- if (err)
- {
- MassStorage::Delete(fullName.c_str(), false);
- reply.catf("Failed to save height map to file %s", fullName.c_str());
- }
- else
- {
- reply.catf("Height map saved to file %s", fullName.c_str());
- }
- }
- return err;
-}
-
-// Save the height map to the file specified by P parameter
-GCodeResult GCodes::SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const
-{
- // No need to check if we're using the SBC interface here, because TrySaveHeightMap does that already
- if (gb.Seen('P'))
- {
- String<MaxFilenameLength> heightMapFileName;
- gb.GetQuotedString(heightMapFileName.GetRef());
- return GetGCodeResultFromError(TrySaveHeightMap(heightMapFileName.c_str(), reply));
- }
- return GetGCodeResultFromError(TrySaveHeightMap(DefaultHeightMapFile, reply));
-}
-
-#endif
-
-// Stop using bed compensation
-void GCodes::ClearBedMapping()
-{
- reprap.GetMove().SetIdentityTransform();
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, 0, reprap.GetCurrentTool());
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // update user coordinates to remove any height map offset there was at the current position
-}
-
-// Return the current coordinates as a printable string.
+// Return the current coordinates as a printable string. Used only to implement M114.
// Coordinates are updated at the end of each movement, so this won't tell you where you are mid-movement.
-void GCodes::GetCurrentCoordinates(const StringRef& s) const noexcept
+void GCodes::HandleM114(GCodeBuffer& gb, const StringRef& s) const noexcept
{
+ const MovementState& ms = GetConstMovementState(gb);
+
// Start with the axis coordinates
s.Clear();
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
// Don't put a space after the colon in the response, it confuses Pronterface
- s.catf("%c:%.3f ", axisLetters[axis], (double)HideNan(GetUserCoordinate(axis)));
+ s.catf("%c:%.3f ", axisLetters[axis], (double)HideNan(GetUserCoordinate(ms, axis)));
}
// Now the virtual extruder position, for Octoprint
- s.catf("E:%.3f ", (double)virtualExtruderPosition);
+ s.catf("E:%.3f ", (double)ms.latestVirtualExtruderPosition);
// Now the extruder coordinates
for (size_t i = 0; i < numExtruders; i++)
{
- s.catf("E%u:%.1f ", i, (double)reprap.GetMove().LiveCoordinate(ExtruderToLogicalDrive(i), reprap.GetCurrentTool()));
+ s.catf("E%u:%.1f ", i, (double)reprap.GetMove().LiveCoordinate(ExtruderToLogicalDrive(i), ms.currentTool));
}
// Print the axis stepper motor positions as Marlin does, as an aid to debugging.
@@ -3104,7 +3092,7 @@ void GCodes::GetCurrentCoordinates(const StringRef& s) const noexcept
// Add the machine coordinates because they may be different from the user coordinates under some conditions
s.cat(" Machine");
float machineCoordinates[MaxAxes];
- ToolOffsetTransform(moveState.currentUserPosition, machineCoordinates);
+ ToolOffsetTransform(moveStates[0], ms.currentUserPosition, machineCoordinates);
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
s.catf(" %.3f", (double)HideNan(machineCoordinates[axis]));
@@ -3112,7 +3100,7 @@ void GCodes::GetCurrentCoordinates(const StringRef& s) const noexcept
// Add the bed compensation
const float machineZ = machineCoordinates[Z_AXIS];
- reprap.GetMove().AxisAndBedTransform(machineCoordinates, reprap.GetCurrentTool(), true);
+ reprap.GetMove().AxisAndBedTransform(machineCoordinates, ms.currentTool, true);
s.catf(" Bed comp %.3f", (double)(machineCoordinates[Z_AXIS] - machineZ));
}
@@ -3136,19 +3124,58 @@ bool GCodes::QueueFileToPrint(const char* fileName, const StringRef& reply) noex
// Start printing the file already selected. We must hold the movement lock and wait for all moves to finish before calling this, because of the call to ResetMoveCounters.
void GCodes::StartPrinting(bool fromStart) noexcept
{
-#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
- fileOffsetToPrint = 0;
+#if (HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES) && SUPPORT_ASYNC_MOVES
+ FileData copyFileToPrint;
+# if HAS_SBC_INTERFACE
+ if (!reprap.UsingSbcInterface())
+# endif
+ {
+ copyFileToPrint.Set(MassStorage::DuplicateOpenHandle(fileToPrint.GetUnderlyingFile()));
+ if (!copyFileToPrint.IsLive())
+ {
+ return;
+ }
+ }
#endif
- restartMoveFractionDone = 0.0;
buildObjects.Init();
+ for (MovementState& ms : moveStates)
+ {
+ ms.InitObjectCancellation();
+ }
+
reprap.GetMove().ResetMoveCounters();
if (fromStart) // if not resurrecting a print
{
- fileGCode->LatestMachineState().volumetricExtrusion = false; // default to non-volumetric extrusion
- virtualExtruderPosition = 0.0;
+ for (MovementState& ms : moveStates)
+ {
+ ms.fileOffsetToPrint = 0;
+ ms.restartMoveFractionDone = 0.0;
+ ms.latestVirtualExtruderPosition = ms.moveStartVirtualExtruderPosition = 0.0;
+ }
+
+ FileGCode()->LatestMachineState().volumetricExtrusion = false; // default to non-volumetric extrusion
+ FileGCode()->LatestMachineState().selectedPlane = 0; // default G2 and G3 moves to XY plane
+#if SUPPORT_ASYNC_MOVES
+ FileGCode()->ExecuteOnlyQueue(0); // only execute commands for movement system 0
+ File2GCode()->LatestMachineState().volumetricExtrusion = false; // default to non-volumetric extrusion
+ File2GCode()->LatestMachineState().selectedPlane = 0; // default G2 and G3 moves to XY plane
+ File2GCode()->ExecuteOnlyQueue(1); // only execute commands for movement system 1
+#endif
+ }
+
+#if HAS_MASS_STORAGE
+# if HAS_SBC_INTERFACE
+ if (!reprap.UsingSbcInterface())
+# endif
+ {
+ fileToPrint.Seek(moveStates[0].fileOffsetToPrint);
+# if SUPPORT_ASYNC_MOVES
+ copyFileToPrint.Seek(moveStates[1].fileOffsetToPrint);
+# endif
}
+#endif
for (size_t extruder = 0; extruder < MaxExtruders; extruder++)
{
@@ -3162,11 +3189,19 @@ void GCodes::StartPrinting(bool fromStart) noexcept
#endif
{
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
- fileGCode->OriginalMachineState().fileState.MoveFrom(fileToPrint);
- fileGCode->GetFileInput()->Reset(fileGCode->OriginalMachineState().fileState);
+ FileGCode()->OriginalMachineState().fileState.MoveFrom(fileToPrint);
+ FileGCode()->GetFileInput()->Reset(FileGCode()->OriginalMachineState().fileState);
+# if SUPPORT_ASYNC_MOVES
+ File2GCode()->OriginalMachineState().fileState.MoveFrom(copyFileToPrint);
+ File2GCode()->GetFileInput()->Reset(File2GCode()->OriginalMachineState().fileState);
+# endif
#endif
}
- fileGCode->StartNewFile();
+
+ FileGCode()->StartNewFile();
+#if SUPPORT_ASYNC_MOVES
+ File2GCode()->StartNewFile();
+#endif
reprap.GetPrintMonitor().StartedPrint();
platform.MessageF(LogWarn,
@@ -3174,12 +3209,17 @@ void GCodes::StartPrinting(bool fromStart) noexcept
reprap.GetPrintMonitor().GetPrintingFilename());
if (fromStart)
{
- fileGCode->LatestMachineState().selectedPlane = 0; // default G2 and G3 moves to XY plane
- DoFileMacro(*fileGCode, START_G, false, AsyncSystemMacroCode); // get fileGCode to execute the start macro so that any M82/M83 codes will be executed in the correct context
+ DoFileMacro(*FileGCode(), START_G, false, AsyncSystemMacroCode); // get fileGCode to execute the start macro so that any M82/M83 codes will be executed in the correct context
+#if SUPPORT_ASYNC_MOVES
+ DoFileMacro(*File2GCode(), START_G, false, AsyncSystemMacroCode); // get file2GCode to execute the start macro so that any M82/M83 codes will be executed in the correct context
+#endif
}
else
{
- fileGCode->LatestMachineState().firstCommandAfterRestart = true;
+ FileGCode()->LatestMachineState().firstCommandAfterRestart = true;
+#if SUPPORT_ASYNC_MOVES
+ File2GCode()->LatestMachineState().firstCommandAfterRestart = true;
+#endif
}
}
@@ -3190,7 +3230,7 @@ GCodeResult GCodes::DoDwell(GCodeBuffer& gb) THROWS(GCodeException)
// This is so that G4 can be used in a trigger or daemon macro file without pausing motion, when the macro doesn't itself command any motion.
if (gb.WasMotionCommanded())
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return GCodeResult::notFinished;
}
@@ -3206,18 +3246,10 @@ GCodeResult GCodes::DoDwell(GCodeBuffer& gb) THROWS(GCodeException)
return GCodeResult::ok;
}
-#if SUPPORT_ROLAND
- // Deal with a Roland configuration
- if (reprap.GetRoland()->Active())
- {
- return reprap.GetRoland()->ProcessDwell(dwell);
- }
-#endif
-
if ( IsSimulating() // if we are simulating then simulate the G4...
- && &gb != daemonGCode // ...unless it comes from the daemon...
- && &gb != triggerGCode // ...or a trigger...
- && (&gb == fileGCode || !exitSimulationWhenFileComplete) // ...or we are simulating a file and this command doesn't come from the file
+ && &gb != DaemonGCode() // ...unless it comes from the daemon...
+ && &gb != TriggerGCode() // ...or a trigger...
+ && (gb.IsFileChannel() || !exitSimulationWhenFileComplete) // ...or we are simulating a file and this command doesn't come from the file
)
{
simulationTime += (float)dwell * 0.001;
@@ -3237,17 +3269,17 @@ ReadLockedPointer<Tool> GCodes::GetSpecifiedOrCurrentTool(GCodeBuffer& gb) THROW
}
else
{
- tNumber = reprap.GetCurrentToolNumber();
+ tNumber = GetMovementState(gb).GetCurrentToolNumber();
if (tNumber < 0)
{
- throw GCodeException(gb.GetLineNumber(), -1, "No tool number given and no current tool");
+ gb.ThrowGCodeException("No tool number given and no current tool");
}
}
- ReadLockedPointer<Tool> tool = reprap.GetTool(tNumber);
+ ReadLockedPointer<Tool> tool = Tool::GetLockedTool(tNumber);
if (tool.IsNull())
{
- throw GCodeException(gb.GetLineNumber(), -1, "Invalid tool number");
+ gb.ThrowGCodeException("Invalid tool number");
}
return tool;
}
@@ -3274,7 +3306,7 @@ GCodeResult GCodes::SetOrReportOffsets(GCodeBuffer &gb, const StringRef& reply,
if (settingOffset)
{
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // update user coordinates to reflect the new tool offset, in case we have this tool selected
+ ToolOffsetInverseTransform(GetMovementState(gb)); // update user coordinates to reflect the new tool offset, in case we have this tool selected
}
}
@@ -3322,7 +3354,7 @@ GCodeResult GCodes::SetOrReportOffsets(GCodeBuffer &gb, const StringRef& reply,
settingOther = true;
if (!IsSimulating())
{
- tool->SetSpindleRpm(gb.GetUIValue());
+ tool->SetSpindleRpm(gb.GetUIValue(), GetMovementState(gb).currentTool == tool.Ptr());
}
}
}
@@ -3519,7 +3551,14 @@ GCodeResult GCodes::ManageTool(GCodeBuffer& gb, const StringRef& reply)
: -1);
// Add or delete tool, so start by deleting the old one with this number, if any
- reprap.DeleteTool(toolNumber);
+ for (MovementState& ms : moveStates)
+ {
+ if (ms.GetCurrentToolNumber() == (int)toolNumber)
+ {
+ ms.SelectTool(-1, false);
+ }
+ }
+ Tool::DeleteTool(toolNumber);
// M563 P# D-1 H-1 [R-1] removes an existing tool
if (dCount == 1 && hCount == 1 && drives[0] == -1 && heaters[0] == -1 && (sCount == 0 || (sCount == 1 && spindleNumber == -1)))
@@ -3533,12 +3572,21 @@ GCodeResult GCodes::ManageTool(GCodeBuffer& gb, const StringRef& reply)
{
return GCodeResult::error;
}
- reprap.AddTool(tool);
+ Tool::AddTool(tool);
}
}
else
{
- reprap.PrintTool(toolNumber, reply);
+ ReadLockedPointer<Tool> const tool = Tool::GetLockedTool(toolNumber);
+ if (tool.IsNotNull())
+ {
+ tool->PrintTool(reply);
+ }
+ else
+ {
+ reply.copy("Error: Attempt to print details of non-existent tool.\n");
+ return GCodeResult::error;
+ }
}
return GCodeResult::ok;
}
@@ -3563,30 +3611,22 @@ bool GCodes::ChangeMicrostepping(size_t axisOrExtruder, unsigned int microsteps,
return success;
}
-// Set the speeds of fans mapped for the current tool to lastDefaultFanSpeed
-void GCodes::SetMappedFanSpeed(float f) noexcept
+// Set the speeds of fans mapped for the current tool to lastDefaultFanSpeed.
+// gbp is nullptr when called from the Display subsystem.
+void GCodes::SetMappedFanSpeed(const GCodeBuffer *null gbp, float f) noexcept
{
- lastDefaultFanSpeed = f;
- const Tool * const ct = reprap.GetCurrentTool();
- if (ct == nullptr)
+ MovementState& ms = (gbp == nullptr) ? moveStates[0] : GetMovementState(*gbp);
+ ms.virtualFanSpeed = f;
+ if (ms.currentTool == nullptr)
{
reprap.GetFansManager().SetFanValue(0, f);
}
else
{
- ct->SetFansPwm(f);
+ ms.currentTool->SetFansPwm(f);
}
}
-// Return true if this fan number is currently being used as a print cooling fan
-bool GCodes::IsMappedFan(unsigned int fanNumber) noexcept
-{
- const Tool * const ct = reprap.GetCurrentTool();
- return (ct == nullptr)
- ? fanNumber == 0
- : ct->GetFanMapping().IsBitSet(fanNumber);
-}
-
// Handle sending a reply back to the appropriate interface(s) and update lastResult
// Note that 'reply' may be empty. If it isn't, then we need to append newline when sending it.
void GCodes::HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char* reply) noexcept
@@ -3631,11 +3671,14 @@ void GCodes::HandleReplyPreserveResult(GCodeBuffer& gb, GCodeResult rslt, const
// Don't report empty responses if a file or macro is being processed, or if the GCode was queued, or to PanelDue
if ( reply[0] == 0
- && ( &gb == fileGCode || &gb == queuedGCode || &gb == triggerGCode || &gb == autoPauseGCode || &gb == daemonGCode
+ && ( gb.IsFileChannel() || &gb == QueuedGCode() || &gb == TriggerGCode() || &gb == AutoPauseGCode() || &gb == DaemonGCode()
+#if SUPPORT_ASYNC_MOVES
+ || &gb == Queue2GCode()
+#endif
#if HAS_AUX_DEVICES
- || (&gb == auxGCode && !platform.IsAuxRaw(0))
+ || (&gb == AuxGCode() && !platform.IsAuxRaw(0))
# ifdef SERIAL_AUX2_DEVICE
- || (&gb == aux2GCode && !platform.IsAuxRaw(1))
+ || (&gb == Aux2GCode() && !platform.IsAuxRaw(1))
# endif
#endif
|| gb.IsDoingFileMacro()
@@ -3655,7 +3698,7 @@ void GCodes::HandleReplyPreserveResult(GCodeBuffer& gb, GCodeResult rslt, const
case Compatibility::Default:
case Compatibility::RepRapFirmware:
// DWC expects a reply from every code, so we must even send empty responses
- if (reply[0] != 0 || gb.IsLastCommand() || &gb == httpGCode)
+ if (reply[0] != 0 || gb.IsLastCommand() || &gb == HttpGCode())
{
platform.MessageF(mt, "%s\n", reply);
}
@@ -3722,7 +3765,11 @@ void GCodes::HandleReply(GCodeBuffer& gb, OutputBuffer *reply) noexcept
#if HAS_AUX_DEVICES
// Second UART device, e.g. dc42's PanelDue. Do NOT use emulation for this one!
- if (&gb == auxGCode && !platform.IsAuxRaw(0))
+ if ( (&gb == AuxGCode() && !platform.IsAuxRaw(0))
+# ifdef SERIAL_AUX2_DEVICE
+ || (&gb == Aux2GCode() && !platform.IsAuxRaw(1))
+# endif
+ )
{
platform.AppendAuxReply(0, reply, (*reply)[0] == '{');
return;
@@ -3796,29 +3843,28 @@ void GCodes::HandleReply(GCodeBuffer& gb, OutputBuffer *reply) noexcept
OutputBuffer::ReleaseAll(reply);
}
-void GCodes::SetToolHeaters(Tool *tool, float temperature, bool both) THROWS(GCodeException)
+// Set all a tool's heaters active and standby temperatures, for M104/M109
+void GCodes::SetToolHeaters(const GCodeBuffer& gb, Tool *tool, float temperature) THROWS(GCodeException)
{
if (tool == nullptr)
{
- throw GCodeException(-1, -1, "setting temperature: no tool selected\n");
+ gb.ThrowGCodeException("setting temperature: no tool selected");
}
for (size_t h = 0; h < tool->HeaterCount(); h++)
{
tool->SetToolHeaterActiveTemperature(h, temperature);
- if (both)
- {
- tool->SetToolHeaterStandbyTemperature(h, temperature);
- }
+ tool->SetToolHeaterStandbyTemperature(h, temperature);
}
}
// Retract or un-retract filament, returning true if movement has been queued, false if this needs to be called again
-GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
+GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract) THROWS(GCodeException)
{
- if (!buildObjects.IsCurrentObjectCancelled())
+ MovementState& ms = GetMovementState(gb);
+ if (!ms.IsCurrentObjectCancelled())
{
- Tool* const currentTool = reprap.GetCurrentTool();
+ Tool* const currentTool = ms.currentTool;
if ( currentTool != nullptr
&& retract != currentTool->IsRetracted()
&& (currentTool->GetRetractLength() != 0.0 || currentTool->GetRetractHop() != 0.0 || (!retract && currentTool->GetRetractExtra() != 0.0))
@@ -3829,61 +3875,74 @@ GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
return GCodeResult::notFinished;
}
- if (moveState.segmentsLeft != 0)
+ if (ms.segmentsLeft != 0)
{
return GCodeResult::notFinished;
}
// New code does the retraction and the Z hop as separate moves
// Get ready to generate a move
- SetMoveBufferDefaults();
- moveState.tool = reprap.GetCurrentTool();
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, 0, moveState.tool);
- moveState.filePos = (&gb == fileGCode) ? gb.GetFilePosition() : noFilePosition;
+ SetMoveBufferDefaults(ms);
+ ms.movementTool = ms.currentTool;
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, 0, ms.movementTool);
+ ms.filePos = gb.GetJobFilePosition();
if (retract)
{
// Set up the retract move
- const Tool * const tool = reprap.GetCurrentTool();
- if (tool != nullptr && tool->DriveCount() != 0)
+ if (currentTool != nullptr && currentTool->DriveCount() != 0)
{
- for (size_t i = 0; i < tool->DriveCount(); ++i)
+ AxesBitmap drivesMoving;
+ for (size_t i = 0; i < currentTool->DriveCount(); ++i)
{
- moveState.coords[ExtruderToLogicalDrive(tool->GetDrive(i))] = -currentTool->GetRetractLength();
+ const size_t logicalDrive = ExtruderToLogicalDrive(currentTool->GetDrive(i));
+ ms.coords[logicalDrive] = -currentTool->GetRetractLength();
+ drivesMoving.SetBit(logicalDrive);
}
- moveState.feedRate = currentTool->GetRetractSpeed() * tool->DriveCount();
- moveState.canPauseAfter = false; // don't pause after a retraction because that could cause too much retraction
- NewSingleSegmentMoveAvailable();
+ ms.feedRate = currentTool->GetRetractSpeed() * currentTool->DriveCount();
+ ms.canPauseAfter = false; // don't pause after a retraction because that could cause too much retraction
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, drivesMoving);
+#endif
+ NewSingleSegmentMoveAvailable(ms);
}
if (currentTool->GetRetractHop() > 0.0)
{
gb.SetState(GCodeState::doingFirmwareRetraction);
}
}
- else if (moveState.currentZHop > 0.0)
+ else if (ms.currentZHop > 0.0)
{
// Set up the reverse Z hop move
- moveState.feedRate = platform.MaxFeedrate(Z_AXIS);
- moveState.coords[Z_AXIS] -= moveState.currentZHop;
- moveState.currentZHop = 0.0;
- moveState.canPauseAfter = false; // don't pause in the middle of a command
- moveState.linearAxesMentioned = true; // assume that both linear and rotational axes might be moving
- NewSingleSegmentMoveAvailable();
+ ms.feedRate = platform.MaxFeedrate(Z_AXIS);
+ ms.coords[Z_AXIS] -= ms.currentZHop;
+ ms.currentZHop = 0.0;
+ ms.canPauseAfter = false; // don't pause in the middle of a command
+ ms.linearAxesMentioned = true;
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, AxesBitmap::MakeFromBits(Z_AXIS));
+#endif
+ NewSingleSegmentMoveAvailable(ms);
gb.SetState(GCodeState::doingFirmwareUnRetraction);
}
else
{
// No retract hop, so just un-retract
- const Tool * const tool = reprap.GetCurrentTool();
- if (tool != nullptr && tool->DriveCount() != 0)
+ if (currentTool != nullptr && currentTool->DriveCount() != 0)
{
- for (size_t i = 0; i < tool->DriveCount(); ++i)
+ AxesBitmap drivesMoving;
+ for (size_t i = 0; i < ms.currentTool->DriveCount(); ++i)
{
- moveState.coords[ExtruderToLogicalDrive(tool->GetDrive(i))] = currentTool->GetRetractLength() + currentTool->GetRetractExtra();
+ const size_t logicalDrive = ExtruderToLogicalDrive(currentTool->GetDrive(i));
+ ms.coords[logicalDrive] = currentTool->GetRetractLength() + currentTool->GetRetractExtra();
+ drivesMoving.SetBit(logicalDrive);
}
- moveState.feedRate = currentTool->GetUnRetractSpeed() * tool->DriveCount();
- moveState.canPauseAfter = true;
- NewSingleSegmentMoveAvailable();
+ ms.feedRate = currentTool->GetUnRetractSpeed() * currentTool->DriveCount();
+ ms.canPauseAfter = true;
+#if SUPPORT_ASYNC_MOVES
+ AllocateAxes(gb, ms, drivesMoving);
+#endif
+ NewSingleSegmentMoveAvailable(ms);
}
}
currentTool->SetRetracted(retract);
@@ -3895,7 +3954,7 @@ GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
// Load the specified filament into a tool
GCodeResult GCodes::LoadFilament(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- Tool * const tool = reprap.GetCurrentTool();
+ Tool * const tool = GetMovementState(gb).currentTool;
if (tool == nullptr)
{
reply.copy("No tool selected");
@@ -3952,7 +4011,7 @@ GCodeResult GCodes::LoadFilament(GCodeBuffer& gb, const StringRef& reply) THROWS
// Unload the current filament from a tool
GCodeResult GCodes::UnloadFilament(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- Tool * const tool = reprap.GetCurrentTool();
+ Tool * const tool = GetMovementState(gb).currentTool;
if (tool == nullptr)
{
reply.copy("No tool selected");
@@ -3980,54 +4039,41 @@ float GCodes::GetRawExtruderTotalByDrive(size_t extruder) const noexcept
return (extruder < numExtruders) ? rawExtruderTotalByDrive[extruder] : 0.0;
}
-// Return true if the code queue is idle
-bool GCodes::IsCodeQueueIdle() const noexcept
-{
- return queuedGCode->IsIdle() && codeQueue->IsIdle();
-}
-
// Cancel the current SD card print.
// This is called from Pid.cpp when there is a heater fault, and from elsewhere in this module.
+// When called to stop a print normally, this is called by fileGCode but not by file2GCode.
void GCodes::StopPrint(StopPrintReason reason) noexcept
{
- moveState.segmentsLeft = 0;
deferredPauseCommandPending = nullptr;
pauseState = PauseState::notPaused;
-#if HAS_SBC_INTERFACE
- if (reprap.UsingSbcInterface())
- {
- fileGCode->ClosePrintFile();
- fileGCode->Init();
- }
- else
+#if HAS_SBC_INTERFACE || HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
+ FileGCode()->ClosePrintFile();
+# if SUPPORT_ASYNC_MOVES
+ File2GCode()->ClosePrintFile();
+# endif
#endif
- {
-#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
- FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
-
- fileGCode->GetFileInput()->Reset(fileBeingPrinted);
- fileGCode->Init();
- if (fileBeingPrinted.IsLive())
- {
- fileBeingPrinted.Close();
- }
+ // Don't call ResetMoveCounters here because we can't be sure that the movement queue is empty
+ UnlockAll(*FileGCode());
+#if SUPPORT_ASYNC_MOVES
+ UnlockAll(*File2GCode());
#endif
- }
-
- // Don't call ReserMoveCounters here because we can't be sure that the movement queue is empty
- codeQueue->Clear();
-
- UnlockAll(*fileGCode);
- // Deal with the Z hop from a G10 that has not been undone by G11
- Tool* const currentTool = reprap.GetCurrentTool();
- if (currentTool != nullptr && currentTool->IsRetracted())
+ for (MovementState& ms : moveStates)
{
- moveState.currentUserPosition[Z_AXIS] += moveState.currentZHop;
- moveState.currentZHop = 0.0;
- currentTool->SetRetracted(false);
+ ms.segmentsLeft = 0;
+ ms.codeQueue->Clear();
+#if SUPPORT_LASER
+ ms.laserPwmOrIoBits.laserPwm = 0;
+#endif
+ // Deal with the Z hop from a G10 that has not been undone by G11
+ if (ms.currentTool != nullptr && ms.currentTool->IsRetracted())
+ {
+ ms.currentUserPosition[Z_AXIS] += ms.currentZHop;
+ ms.currentZHop = 0.0;
+ ms.currentTool->SetRetracted(false);
+ }
}
const char *printingFilename = reprap.GetPrintMonitor().GetPrintingFilename();
@@ -4047,6 +4093,7 @@ void GCodes::StopPrint(StopPrintReason reason) noexcept
#endif
exitSimulationWhenFileComplete = false;
+ updateFileWhenSimulationComplete = false;
simulationMode = SimulationMode::off; // do this after we append the simulation info to the file so that DWC doesn't try to reload the file info too soon
reprap.GetMove().Simulate(simulationMode);
EndSimulation(nullptr);
@@ -4082,7 +4129,6 @@ void GCodes::StopPrint(StopPrintReason reason) noexcept
#if SUPPORT_LASER
case MachineType::laser:
platform.SetLaserPwm(0);
- moveState.laserPwmOrIoBits.laserPwm = 0;
break;
#endif
@@ -4092,12 +4138,12 @@ void GCodes::StopPrint(StopPrintReason reason) noexcept
}
// Pronterface expects a "Done printing" message
- if (usbGCode->LatestMachineState().compatibility == Compatibility::Marlin)
+ if (UsbGCode()->LatestMachineState().compatibility == Compatibility::Marlin)
{
platform.Message(UsbMessage, "Done printing file\n");
}
#if SUPPORT_TELNET
- if (telnetGCode->LatestMachineState().compatibility == Compatibility::Marlin)
+ if (TelnetGCode()->LatestMachineState().compatibility == Compatibility::Marlin)
{
platform.Message(TelnetMessage, "Done printing file\n");
}
@@ -4109,17 +4155,23 @@ void GCodes::StopPrint(StopPrintReason reason) noexcept
(reason == StopPrintReason::normalCompletion) ? "Finished" : "Cancelled",
printingFilename, printMinutes/60u, printMinutes % 60u);
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
- if (reason == StopPrintReason::normalCompletion && !IsSimulating())
+ if (reason == StopPrintReason::normalCompletion)
{
platform.DeleteSysFile(RESUME_AFTER_POWER_FAIL_G);
+ if (FileGCode()->GetState() == GCodeState::normal) // this should always be the case
+ {
+ FileGCode()->SetState(GCodeState::stopping); // set fileGCode (which should be the one calling this) to run stop.g
+ }
}
#endif
}
- updateFileWhenSimulationComplete = false;
- reprap.GetPrintMonitor().StoppedPrint(); // must do this after printing the simulation details not before, because it clears the filename and pause time
+ reprap.GetPrintMonitor().StoppedPrint(); // must do this after printing the simulation details not before, because it clears the filename and pause time
buildObjects.Init();
- fileGCode->LatestMachineState().variables.Clear(); // delete any local variables that the file created
+ FileGCode()->LatestMachineState().variables.Clear(); // delete any local variables that the file created
+#if SUPPORT_ASYNC_MOVES
+ File2GCode()->LatestMachineState().variables.Clear(); // delete any local variables that the file created
+#endif
}
// Return true if all the heaters for the specified tool are at their set temperatures
@@ -4141,42 +4193,38 @@ bool GCodes::ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling
// Get the current position from the Move class
void GCodes::UpdateCurrentUserPosition(const GCodeBuffer& gb) noexcept
{
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, 0, reprap.GetCurrentTool());
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
+ MovementState& ms = GetMovementState(gb);
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, 0, ms.currentTool);
+ UpdateUserPositionFromMachinePosition(gb, ms);
+}
+
+// Update the user position from the machine position
+void GCodes::UpdateUserPositionFromMachinePosition(const GCodeBuffer& gb, MovementState& ms) noexcept
+{
+ ToolOffsetInverseTransform(ms);
#if SUPPORT_COORDINATE_ROTATION
if (g68Angle != 0.0 && gb.DoingCoordinateRotation())
{
- RotateCoordinates(-g68Angle, moveState.currentUserPosition);
+ RotateCoordinates(-g68Angle, ms.currentUserPosition);
}
#endif
}
// Save position etc. to a restore point.
// Note that restore point coordinates are not affected by workplace coordinate offsets. This allows them to be used in resume.g.
-void GCodes::SavePosition(RestorePoint& rp, const GCodeBuffer& gb) const noexcept
+void GCodes::SavePosition(const GCodeBuffer& gb, unsigned int restorePointNumber) noexcept
{
- for (size_t axis = 0; axis < numVisibleAxes; ++axis)
- {
- rp.moveCoords[axis] = moveState.currentUserPosition[axis];
- }
-
- rp.feedRate = gb.LatestMachineState().feedRate;
- rp.virtualExtruderPosition = virtualExtruderPosition;
- rp.filePos = gb.GetFilePosition();
- rp.toolNumber = reprap.GetCurrentToolNumber();
- rp.fanSpeed = lastDefaultFanSpeed;
-
-#if SUPPORT_LASER || SUPPORT_IOBITS
- rp.laserPwmOrIoBits = moveState.laserPwmOrIoBits;
-#endif
+ MovementState& ms = GetMovementState(gb);
+ ms.SavePosition(restorePointNumber, numVisibleAxes, gb.LatestMachineState().feedRate, gb.GetJobFilePosition());
}
// Restore user position from a restore point. Also restore the laser power, but not the spindle speed (the user must do that explicitly).
void GCodes::RestorePosition(const RestorePoint& rp, GCodeBuffer *gb) noexcept
{
+ MovementState& ms = (gb == nullptr) ? moveStates[0] : GetMovementState(*gb); //TODO handle null gb properly!
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- moveState.currentUserPosition[axis] = rp.moveCoords[axis];
+ ms.currentUserPosition[axis] = rp.moveCoords[axis];
}
if (gb != nullptr)
@@ -4184,21 +4232,20 @@ void GCodes::RestorePosition(const RestorePoint& rp, GCodeBuffer *gb) noexcept
gb->LatestMachineState().feedRate = rp.feedRate;
}
- moveState.initialUserC0 = rp.initialUserC0;
- moveState.initialUserC1 = rp.initialUserC1;
+ ms.initialUserC0 = rp.initialUserC0;
+ ms.initialUserC1 = rp.initialUserC1;
#if SUPPORT_LASER || SUPPORT_IOBITS
- moveState.laserPwmOrIoBits = rp.laserPwmOrIoBits;
+ ms.laserPwmOrIoBits = rp.laserPwmOrIoBits;
#endif
}
// Convert user coordinates to head reference point coordinates, optionally allowing for X axis mapping
// If the X axis is mapped to some other axes not including X, then the X coordinate of coordsOut will be left unchanged.
// So make sure it is suitably initialised before calling this.
-void GCodes::ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes], AxesBitmap explicitAxes) const noexcept
+void GCodes::ToolOffsetTransform(const MovementState& ms, const float coordsIn[MaxAxes], float coordsOut[MaxAxes], AxesBitmap explicitAxes) const noexcept
{
- const Tool * const currentTool = reprap.GetCurrentTool();
- if (currentTool == nullptr)
+ if (ms.currentTool == nullptr)
{
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
@@ -4207,15 +4254,15 @@ void GCodes::ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[
}
else
{
- const AxesBitmap xAxes = currentTool->GetXAxisMap();
- const AxesBitmap yAxes = currentTool->GetYAxisMap();
+ const AxesBitmap xAxes = ms.currentTool->GetXAxisMap();
+ const AxesBitmap yAxes = ms.currentTool->GetYAxisMap();
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
if ( (axis != X_AXIS || xAxes.IsBitSet(X_AXIS))
&& (axis != Y_AXIS || yAxes.IsBitSet(Y_AXIS))
)
{
- const float totalOffset = currentBabyStepOffsets[axis] - currentTool->GetOffset(axis);
+ const float totalOffset = currentBabyStepOffsets[axis] - ms.currentTool->GetOffset(axis);
const size_t inputAxis = (explicitAxes.IsBitSet(axis)) ? axis
: (xAxes.IsBitSet(axis)) ? X_AXIS
: (yAxes.IsBitSet(axis)) ? Y_AXIS
@@ -4224,15 +4271,20 @@ void GCodes::ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[
}
}
}
- coordsOut[Z_AXIS] += moveState.currentZHop;
+ coordsOut[Z_AXIS] += ms.currentZHop;
}
-// Convert head reference point coordinates to user coordinates, allowing for XY axis mapping
+// Convert user coordinates to head reference point coordinates
+void GCodes::ToolOffsetTransform(MovementState& ms, AxesBitmap explicitAxes) const noexcept
+{
+ ToolOffsetTransform(ms, ms.currentUserPosition, ms.coords, explicitAxes);
+}
+
+// Convert head reference point coordinates to user coordinates
// Caution: coordsIn and coordsOut may address the same array!
-void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes]) const noexcept
+void GCodes::ToolOffsetInverseTransform(const MovementState& ms, const float coordsIn[MaxAxes], float coordsOut[MaxAxes]) const noexcept
{
- const Tool * const currentTool = reprap.GetCurrentTool();
- if (currentTool == nullptr)
+ if (ms.currentTool == nullptr)
{
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
@@ -4241,13 +4293,13 @@ void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coo
}
else
{
- const AxesBitmap xAxes = reprap.GetCurrentXAxes();
- const AxesBitmap yAxes = reprap.GetCurrentYAxes();
+ const AxesBitmap xAxes = ms.GetCurrentXAxes();
+ const AxesBitmap yAxes = ms.GetCurrentYAxes();
float xCoord = 0.0, yCoord = 0.0;
size_t numXAxes = 0, numYAxes = 0;
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- const float totalOffset = currentBabyStepOffsets[axis] - currentTool->GetOffset(axis);
+ const float totalOffset = currentBabyStepOffsets[axis] - ms.currentTool->GetOffset(axis);
const float coord = (coordsIn[axis] - totalOffset)/axisScaleFactors[axis];
coordsOut[axis] = coord;
if (xAxes.IsBitSet(axis))
@@ -4270,20 +4322,19 @@ void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coo
coordsOut[Y_AXIS] = yCoord/numYAxes;
}
}
- coordsOut[Z_AXIS] -= moveState.currentZHop/axisScaleFactors[Z_AXIS];
+ coordsOut[Z_AXIS] -= ms.currentZHop/axisScaleFactors[Z_AXIS];
}
-// Get an axis offset of the current tool
-float GCodes::GetCurrentToolOffset(size_t axis) const noexcept
+// Convert head reference point coordinates to user coordinates, allowing for XY axis mapping
+void GCodes::ToolOffsetInverseTransform(MovementState& ms) const noexcept
{
- const Tool* const tool = reprap.GetCurrentTool();
- return (tool == nullptr) ? 0.0 : tool->GetOffset(axis);
+ ToolOffsetInverseTransform(ms, ms.coords, ms.currentUserPosition);
}
// Get the current user coordinate and remove the coordinate rotation and workplace offset
-float GCodes::GetUserCoordinate(size_t axis) const noexcept
+float GCodes::GetUserCoordinate(const MovementState& ms, size_t axis) const noexcept
{
- return (axis < numTotalAxes) ? moveState.currentUserPosition[axis] - GetWorkplaceOffset(axis) : 0.0;
+ return (axis < numTotalAxes) ? ms.currentUserPosition[axis] - GetWorkplaceOffset(axis, ms.currentCoordinateSystem) : 0.0;
}
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
@@ -4436,7 +4487,7 @@ GCodeResult GCodes::WriteConfigOverrideFile(GCodeBuffer& gb, const StringRef& re
if (ok)
{
- ok = reprap.WriteToolParameters(f, p10);
+ ok = WriteToolParameters(f, p10);
}
#if SUPPORT_WORKPLACE_COORDINATES
@@ -4491,9 +4542,19 @@ bool GCodes::WriteConfigOverrideHeader(FileStore *f) const noexcept
#endif
// Store a M105-format temperature report in 'reply'. This doesn't put a newline character at the end.
-void GCodes::GenerateTemperatureReport(const StringRef& reply) const noexcept
+void GCodes::GenerateTemperatureReport(const GCodeBuffer& gb, const StringRef& reply) const noexcept
{
- reprap.ReportAllToolTemperatures(reply);
+ {
+ ReadLocker lock(Tool::toolListLock);
+
+ // The following is believed to be compatible with Marlin and Octoprint, based on thread https://github.com/foosel/OctoPrint/issues/2590#issuecomment-385023980
+ ReportToolTemperatures(reply, GetConstMovementState(gb).currentTool, false);
+
+ for (const Tool *tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next())
+ {
+ ReportToolTemperatures(reply, tool, true);
+ }
+ }
Heat& heat = reprap.GetHeat();
for (size_t hn = 0; hn < MaxBedHeaters && heat.GetBedHeater(hn) >= 0; ++hn)
@@ -4537,12 +4598,12 @@ void GCodes::GenerateTemperatureReport(const StringRef& reply) const noexcept
// 'reply' is a convenient buffer that is free for us to use.
void GCodes::CheckReportDue(GCodeBuffer& gb, const StringRef& reply) const noexcept
{
- if (&gb == usbGCode)
+ if (&gb == UsbGCode())
{
if (gb.LatestMachineState().compatibility == Compatibility::Marlin && gb.IsReportDue())
{
// In Marlin emulation mode we should return a standard temperature report every second
- GenerateTemperatureReport(reply);
+ GenerateTemperatureReport(gb, reply);
if (reply.strlen() > 0)
{
reply.cat('\n');
@@ -4551,7 +4612,7 @@ void GCodes::CheckReportDue(GCodeBuffer& gb, const StringRef& reply) const noexc
}
}
}
- else if (&gb == auxGCode)
+ else if (&gb == AuxGCode())
{
if (lastAuxStatusReportType >= 0 && platform.IsAuxEnabled(0) && gb.IsReportDue())
{
@@ -4608,18 +4669,18 @@ OutputBuffer *GCodes::GenerateJsonStatusResponse(int type, int seq, ResponseSour
return statusResponse;
}
-// Initiate a tool change. Caller has already checked that the correct tool isn't loaded.
-void GCodes::StartToolChange(GCodeBuffer& gb, int toolNum, uint8_t param) noexcept
+// Initiate a tool change. Caller has already checked that the correct tool isn't loaded and set up ms.newToolNumber.
+void GCodes::StartToolChange(GCodeBuffer& gb, uint8_t param) noexcept
{
- newToolNumber = toolNum;
- toolChangeParam = (IsSimulating()) ? 0 : param;
+ MovementState& ms = GetMovementState(gb);
+ ms.toolChangeParam = (IsSimulating()) ? 0 : param;
gb.SetState(GCodeState::toolChange0);
}
// Set up some default values in the move buffer for special moves, e.g. for Z probing and firmware retraction
-void GCodes::SetMoveBufferDefaults() noexcept
+void GCodes:: SetMoveBufferDefaults(MovementState& ms) noexcept
{
- moveState.SetDefaults(numTotalAxes);
+ ms.SetDefaults(numTotalAxes);
}
// Resource locking/unlocking
@@ -4662,22 +4723,55 @@ bool GCodes::LockFileSystem(const GCodeBuffer &gb) noexcept
return LockResource(gb, FileSystemResource);
}
+// The movement lock is special because we have one for each motion system
// Lock movement
bool GCodes::LockMovement(const GCodeBuffer& gb) noexcept
{
- return LockResource(gb, MoveResource);
+#if SUPPORT_ASYNC_MOVES
+ return LockResource(gb, MoveResourceBase + gb.GetQueueNumberToLock());
+#else
+ return LockResource(gb, MoveResourceBase);
+#endif
+}
+
+// Lock movement on all motion systems
+bool GCodes::LockAllMovement(const GCodeBuffer& gb) noexcept
+{
+#if SUPPORT_ASYNC_MOVES
+ for (Resource r = MoveResourceBase; r < MoveResourceBase + NumMovementSystems; ++r)
+ {
+ if (!LockResource(gb, r))
+ {
+ return false;
+ }
+ }
+ return true;
+#else
+ return LockMovement(gb);
+#endif
}
// Grab the movement lock even if another channel owns it
void GCodes::GrabMovement(const GCodeBuffer& gb) noexcept
{
- GrabResource(gb, MoveResource);
+#if SUPPORT_ASYNC_MOVES
+ GrabResource(gb, MoveResourceBase + gb.GetQueueNumberToLock());
+#else
+ GrabResource(gb, MoveResourceBase);
+#endif
}
// Release the movement lock
void GCodes::UnlockMovement(const GCodeBuffer& gb) noexcept
{
- UnlockResource(gb, MoveResource);
+#if SUPPORT_ASYNC_MOVES
+ for (Resource r = MoveResourceBase; r < MoveResourceBase + NumMovementSystems; ++r)
+ {
+ UnlockResource(gb, r);
+ }
+#else
+ UnlockResource(gb, MoveResourceBase);
+#endif
}
// Unlock the resource if we own it
@@ -4704,7 +4798,7 @@ void GCodes::UnlockAll(const GCodeBuffer& gb) noexcept
{
if (resourceOwners[i] == &gb && !resourcesToKeep.IsBitSet(i))
{
- if (i == MoveResource)
+ if (i >= MoveResourceBase && i < MoveResourceBase + NumResources)
{
// In case homing was aborted because of an exception, we need to clear toBeHomed when releasing the movement lock
toBeHomed.Clear();
@@ -4766,8 +4860,11 @@ void GCodes::ActivateHeightmap(bool activate) noexcept
if (activate)
{
// Update the current position to allow for any bed compensation at the current XY coordinates
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, 0, reprap.GetCurrentTool());
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // update user coordinates to reflect any height map offset at the current position
+ for (MovementState& ms : moveStates)
+ {
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, 0, ms.currentTool);
+ ToolOffsetInverseTransform(ms); // update user coordinates to reflect any height map offset at the current position
+ }
}
}
@@ -4794,6 +4891,125 @@ bool GCodes::CheckNetworkCommandAllowed(GCodeBuffer& gb, const StringRef& reply,
return true;
}
+#if SUPPORT_ASYNC_MOVES
+
+// Get a reference to the movement state associated with the specified GCode buffer
+MovementState& GCodes::GetMovementState(const GCodeBuffer& gb) noexcept
+{
+ return moveStates[gb.GetActiveQueueNumber()];
+}
+
+// Get a reference to the movement state associated with the specified GCode buffer
+const MovementState& GCodes::GetConstMovementState(const GCodeBuffer& gb) const noexcept
+{
+ return moveStates[gb.GetActiveQueueNumber()];
+}
+
+const MovementState& GCodes::GetCurrentMovementState(const ObjectExplorationContext& context) const noexcept
+{
+ const GCodeBuffer *gb = context.GetGCodeBuffer();
+ if (gb == nullptr)
+ {
+#if HAS_NETWORKING
+ gb = HttpGCode(); // assume the request came from the network
+#else
+ return moveStates[0];
+#endif
+ }
+ return GetConstMovementState(*gb);
+}
+
+// Allocate an axis to a movement state returning true if successful, false if another movement state owns it already
+// This relies on cooperative scheduling between different GCodeBuffer objects
+void GCodes::AllocateAxes(const GCodeBuffer& gb, MovementState& ms, AxesBitmap axes) THROWS(GCodeException)
+{
+ if ((axes & axesAndExtrudersMoved & ~ms.axesAndExtrudersOwned).IsNonEmpty())
+ {
+ gb.ThrowGCodeException("Axis is already used by a different motion system");
+ }
+ axesAndExtrudersMoved |= axes;
+ ms.axesAndExtrudersOwned |= axes;
+}
+
+// Synchronise motion systems and update user coordinates.
+// This is called after we have checked that the motion system for thisGb has completed all moves.
+// Return true if synced, false if we need to wait longer.
+bool GCodes::SyncWith(GCodeBuffer& thisGb, const GCodeBuffer& otherGb) noexcept
+{
+ switch (thisGb.syncState)
+ {
+ case GCodeBuffer::SyncState::running:
+ thisGb.syncState = GCodeBuffer::SyncState::syncing; // tell other input channels that we are waiting for sync
+ // no break
+ case GCodeBuffer::SyncState::syncing:
+ if (otherGb.syncState == GCodeBuffer::SyncState::running)
+ {
+ // The other input channel has either not caught up with us, or it has skipped this sync point, or it is not running
+ if (otherGb.IsLaterThan(thisGb))
+ {
+ // Other input channel has skipped this sync point
+ UpdateUserCoordinatesAndReleaseOwnedAxes(thisGb, otherGb); // it's arguable whether updating machine coordinates from the other channel is worth doing here
+ thisGb.syncState = GCodeBuffer::SyncState::running;
+ return true;
+ }
+ // Other input channel has not caught up with us yet, so wait for it
+ return false;
+ }
+
+ // If we get here then the other input channel is also syncing, so it's safe to use the machine axis coordinates of the axes it owns to update our user coordinates
+ UpdateUserCoordinatesAndReleaseOwnedAxes(thisGb, otherGb);
+
+ // Now that we no longer need to read axis coordinates from the other motion system, flag that we have finished syncing
+ thisGb.syncState = GCodeBuffer::SyncState::synced;
+ return false;
+
+ case GCodeBuffer::SyncState::synced:
+ switch (otherGb.syncState)
+ {
+ case GCodeBuffer::SyncState::running:
+ // Other input channel has carried on. If we are not the primary, wait until it has completed the command
+ return thisGb.IsExecuting() || otherGb.IsLaterThan(thisGb);
+
+ case GCodeBuffer::SyncState::syncing:
+ // Other input channel hasn't noticed that we are fully synced yet
+ return false;
+
+ case GCodeBuffer::SyncState::synced:
+ // We are fully synchronised now, so we can finish syncing
+ if (thisGb.IsExecuting())
+ {
+ // We are the executing input stream, so we can carry on
+ thisGb.syncState = GCodeBuffer::SyncState::running;
+ return true;
+ }
+ // We are not the primary, so wait for the other onput channel to complete the current command
+ return otherGb.IsLaterThan(thisGb);
+ }
+ }
+
+ return false; // unreachable code, to keep Eclipse happy
+}
+
+void GCodes::UpdateUserCoordinatesAndReleaseOwnedAxes(GCodeBuffer& thisGb, const GCodeBuffer& otherGb) noexcept
+{
+ // Get the position of all axes by combining positions from the queues
+ const MovementState& otherMs = GetConstMovementState(otherGb);
+ Move& move = reprap.GetMove();
+ float coords[MaxAxes];
+ move.GetPartialMachinePosition(coords, AxesBitmap::MakeLowestNBits(numTotalAxes), thisGb.GetOwnQueueNumber());
+ move.GetPartialMachinePosition(coords, otherMs.axesAndExtrudersOwned, otherGb.GetOwnQueueNumber());
+ MovementState& thisMs = GetMovementState(thisGb);
+ move.InverseAxisAndBedTransform(coords, thisMs.currentTool);
+ UpdateUserPositionFromMachinePosition(thisGb, thisMs);
+ move.SetNewPosition(coords, true, thisGb.GetOwnQueueNumber());
+
+ // Release all axes and extruders that we were using
+ axesAndExtrudersMoved &= ~thisMs.axesAndExtrudersOwned;
+ thisMs.axesAndExtrudersOwned.Clear();
+}
+
+#endif
+
#if HAS_MASS_STORAGE
// Start timing SD card file writing
@@ -4819,12 +5035,12 @@ GCodeResult GCodes::StartSDTiming(GCodeBuffer& gb, const StringRef& reply) noexc
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
// Set the speed factor. Value passed is a fraction.
-void GCodes::SetSpeedFactor(float factor) noexcept
+void GCodes::SetPrimarySpeedFactor(float factor) noexcept
{
- speedFactor = constrain<float>(factor, 0.1, 5.0);
+ moveStates[0].speedFactor = constrain<float>(factor, 0.1, 5.0);
}
// Set an extrusion factor
@@ -4839,9 +5055,9 @@ void GCodes::SetExtrusionFactor(size_t extruder, float factor) noexcept
// Process a GCode command from the 12864 LCD returning true if the command was accepted
bool GCodes::ProcessCommandFromLcd(const char *cmd) noexcept
{
- if (lcdGCode->IsCompletelyIdle())
+ if (LcdGCode()->IsCompletelyIdle())
{
- lcdGCode->PutAndDecode(cmd);
+ LcdGCode()->PutAndDecode(cmd);
return true;
}
return false;
@@ -4851,7 +5067,7 @@ int GCodes::GetHeaterNumber(unsigned int itemNumber) const noexcept
{
if (itemNumber < 80)
{
- ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? reprap.GetLockedCurrentTool() : reprap.GetTool(itemNumber);
+ ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? GetPrimaryMovementState().GetLockedCurrentTool() : Tool::GetLockedTool(itemNumber);
return (tool.IsNotNull() && tool->HeaterCount() != 0) ? tool->GetHeater(0) : -1;
}
if (itemNumber < 90)
@@ -4870,7 +5086,7 @@ float GCodes::GetItemActiveTemperature(unsigned int itemNumber) const noexcept
{
if (itemNumber < 80)
{
- ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? reprap.GetLockedCurrentTool() : reprap.GetTool(itemNumber);
+ ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? GetPrimaryMovementState().GetLockedCurrentTool() : Tool::GetLockedTool(itemNumber);
return (tool.IsNotNull()) ? tool->GetToolHeaterActiveTemperature(0) : 0.0;
}
@@ -4881,7 +5097,7 @@ float GCodes::GetItemStandbyTemperature(unsigned int itemNumber) const noexcept
{
if (itemNumber < 80)
{
- ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? reprap.GetLockedCurrentTool() : reprap.GetTool(itemNumber);
+ ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? GetPrimaryMovementState().GetLockedCurrentTool() : Tool::GetLockedTool(itemNumber);
return (tool.IsNotNull()) ? tool->GetToolHeaterStandbyTemperature(0) : 0.0;
}
@@ -4892,11 +5108,11 @@ void GCodes::SetItemActiveTemperature(unsigned int itemNumber, float temp) noexc
{
if (itemNumber < 80)
{
- ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? reprap.GetLockedCurrentTool() : reprap.GetTool(itemNumber);
+ ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? GetPrimaryMovementState().GetLockedCurrentTool() : Tool::GetLockedTool(itemNumber);
if (tool.IsNotNull())
{
tool->SetToolHeaterActiveTemperature(0, temp);
- if (tool->Number() == reprap.GetCurrentToolNumber() && temp > NEARLY_ABS_ZERO)
+ if (tool->Number() == GetPrimaryMovementState().GetCurrentToolNumber() && temp > NEARLY_ABS_ZERO)
{
tool->HeatersToActiveOrStandby(true); // if it's the current tool then make sure it is active
}
@@ -4918,7 +5134,7 @@ void GCodes::SetItemStandbyTemperature(unsigned int itemNumber, float temp) noex
{
if (itemNumber < 80)
{
- ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? reprap.GetLockedCurrentTool() : reprap.GetTool(itemNumber);
+ ReadLockedPointer<Tool> const tool = (itemNumber == 79) ? GetPrimaryMovementState().GetLockedCurrentTool() : Tool::GetLockedTool(itemNumber);
if (tool.IsNotNull())
{
tool->SetToolHeaterStandbyTemperature(0, temp);
@@ -4930,6 +5146,36 @@ void GCodes::SetItemStandbyTemperature(unsigned int itemNumber, float temp) noex
}
}
+// Evalue a visibility expression string
+bool GCodes::EvaluateConditionForDisplay(const char *_ecv_array str) const noexcept
+{
+ try
+ {
+ ExpressionParser parser(*LcdGCode(), str, str + strlen(str));
+ return parser.ParseBoolean();
+ }
+ catch (GCodeException&)
+ {
+ return false;
+ }
+}
+
+// Evaluate a value string returning true if an error occurred
+bool GCodes::EvaluateValueForDisplay(const char *_ecv_array str, ExpressionValue& expr) const noexcept
+{
+ try
+ {
+ ExpressionParser parser(*LcdGCode(), str, str + strlen(str));
+ expr = parser.Parse();
+ return true;
+ }
+ catch (GCodeException&)
+ {
+ expr.SetNull(nullptr);
+ return false;
+ }
+}
+
#endif
// End
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index 84969541..e495349a 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -32,6 +32,7 @@ Licence: GPL
#include "GCodeChannel.h"
#include "GCodeInput.h"
#include "GCodeMachineState.h"
+#include <GCodes/CollisionAvoider.h>
#include <GCodes/TriggerItem.h>
#include <Tools/Filament.h>
#include <FilamentMonitors/FilamentMonitor.h>
@@ -42,12 +43,6 @@ Licence: GPL
const char feedrateLetter = 'F'; // GCode feedrate
const char extrudeLetter = 'E'; // GCode extrude
-// Bits for T-code P-parameter to specify which macros are supposed to be run
-constexpr uint8_t TFreeBit = 1u << 0;
-constexpr uint8_t TPreBit = 1u << 1;
-constexpr uint8_t TPostBit = 1u << 2;
-constexpr uint8_t DefaultToolChangeParam = TFreeBit | TPreBit | TPostBit;
-
// Machine type enumeration. The numeric values must be in the same order as the corresponding M451..M453 commands.
enum class MachineType : uint8_t
{
@@ -110,18 +105,18 @@ public:
void Init() noexcept; // Set it up
void Exit() noexcept; // Shut it down
void Reset() noexcept; // Reset some parameter to defaults
- bool ReadMove(RawMove& m) noexcept; // Called by the Move class to get a movement set by the last G Code
- void ClearMove() noexcept;
+ bool ReadMove(unsigned int queueNumber, RawMove& m) noexcept
+ pre(queueNumber < ARRAY_SIZE(moveStates)); // Called by the Move class to get a movement set by the last G Code
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
bool QueueFileToPrint(const char* fileName, const StringRef& reply) noexcept; // Open a file of G Codes to run
#endif
void AbortPrint(GCodeBuffer& gb) noexcept; // Cancel any print in progress
- void GetCurrentCoordinates(const StringRef& s) const noexcept; // Write where we are into a string
+ void HandleM114(GCodeBuffer& gb, const StringRef& s) const noexcept; // Write where we are into a string
bool DoingFileMacro() const noexcept; // Is a macro file being processed by any input channel?
bool GetMacroRestarted() const noexcept; // Return true if the macro being executed by fileGCode was restarted
bool WaitingForAcknowledgement() const noexcept; // Is an input waiting for a message to be acknowledged?
- FilePosition GetFilePosition(bool allowNoFilePos = false) const noexcept; // Return the current position of the file being printed in bytes. May return noFilePosition if allowNoFilePos is true
+ FilePosition GetPrintingFilePosition() const noexcept; // Return the current position of the file being printed in bytes. May return noFilePosition if allowNoFilePos is true
void Diagnostics(MessageType mtype) noexcept; // Send helpful information out
bool RunConfigFile(const char* fileName) noexcept; // Start running the config file
@@ -133,18 +128,15 @@ public:
void SetAxisNotHomed(unsigned int axis) noexcept; // Tell us that the axis is not homed
void SetAllAxesNotHomed() noexcept; // Flag all axes as not homed
- float GetSpeedFactor() const noexcept { return speedFactor; } // Return the current speed factor as a fraction
+ float GetPrimarySpeedFactor() const noexcept { return moveStates[0].speedFactor; } // Return the current speed factor as a fraction
float GetExtrusionFactor(size_t extruder) noexcept; // Return the current extrusion factor for the specified extruder
-#if SUPPORT_12864_LCD
- void SetSpeedFactor(float factor) noexcept; // Set the speed factor
- void SetExtrusionFactor(size_t extruder, float factor) noexcept; // Set an extrusion factor for the specified extruder
-#endif
-
+ float GetFilamentDiameter(size_t extruder) const noexcept
+ pre(extruder < MaxExtruders) { return filamentDiameters[extruder]; }
float GetRawExtruderTotalByDrive(size_t extruder) const noexcept; // Get the total extrusion since start of print, for one drive
float GetTotalRawExtrusion() const noexcept { return rawExtruderTotal; } // Get the total extrusion since start of print, all drives
float GetTotalBabyStepOffset(size_t axis) const noexcept
pre(axis < maxAxes);
- float GetUserCoordinate(size_t axis) const noexcept; // Get the current user coordinate in the current workspace coordinate system
+ float GetUserCoordinate(const MovementState& ms, size_t axis) const noexcept; // Get the current user coordinate in the current workspace coordinate system
bool CheckNetworkCommandAllowed(GCodeBuffer& gb, const StringRef& reply, GCodeResult& result) noexcept;
#if HAS_NETWORKING
@@ -195,49 +187,58 @@ public:
const char *GetAxisLetters() const noexcept { return axisLetters; } // Return a null-terminated string of axis letters indexed by drive
size_t GetAxisNumberForLetter(const char axisLetter) const noexcept;
MachineType GetMachineType() const noexcept { return machineType; }
- bool LockMovementAndWaitForStandstill(GCodeBuffer& gb) noexcept; // Lock movement and wait for pending moves to finish
+ bool LockMovementAndWaitForStandstill(GCodeBuffer& gb
+#if SUPPORT_ASYNC_MOVES
+ , bool sync = true
+#endif
+ ) noexcept; // Lock movement and wait for pending moves to finish
+ bool LockMovementAndWaitForStandstillNoSync(GCodeBuffer& gb) noexcept; // Lock movement and wait for pending moves to finish but don't sync if using multiple movement queues
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
+ void SetPrimarySpeedFactor(float factor) noexcept; // Set the speed factor
+ void SetExtrusionFactor(size_t extruder, float factor) noexcept; // Set an extrusion factor for the specified extruder
+ void SelectPrimaryTool(int toolNumber, bool simulating) noexcept { moveStates[0].SelectTool(toolNumber, simulating); }
bool ProcessCommandFromLcd(const char *cmd) noexcept; // Process a GCode command from the 12864 LCD returning true if the command was accepted
float GetItemCurrentTemperature(unsigned int itemNumber) const noexcept;
float GetItemActiveTemperature(unsigned int itemNumber) const noexcept;
float GetItemStandbyTemperature(unsigned int itemNumber) const noexcept;
void SetItemActiveTemperature(unsigned int itemNumber, float temp) noexcept;
void SetItemStandbyTemperature(unsigned int itemNumber, float temp) noexcept;
+ bool EvaluateConditionForDisplay(const char *_ecv_array str) const noexcept;
+ bool EvaluateValueForDisplay(const char *_ecv_array str, ExpressionValue& expr) const noexcept;
#endif
- float GetMappedFanSpeed() const noexcept { return lastDefaultFanSpeed; } // Get the mapped fan speed
- void SetMappedFanSpeed(float f) noexcept; // Set the speeds of fans mapped for the current tool
+ void SetMappedFanSpeed(const GCodeBuffer *null gb, float f) noexcept; // Set the speeds of fans mapped for the current tool
void HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char *reply) noexcept; // Handle G-Code replies
- void EmergencyStop() noexcept; // Cancel everything
+ void EmergencyStop() noexcept; // Cancel everything
- const GridDefinition& GetDefaultGrid() const { return defaultGrid; }; // Get the default grid definition
- void ActivateHeightmap(bool activate) noexcept; // (De-)Activate the height map
+ const GridDefinition& GetDefaultGrid() const { return defaultGrid; }; // Get the default grid definition
+ void ActivateHeightmap(bool activate) noexcept; // (De-)Activate the height map
- int GetNewToolNumber() const noexcept { return newToolNumber; }
size_t GetCurrentZProbeNumber() const noexcept { return currentZProbeNumber; }
// These next two are public because they are used by class SbcInterface
- void UnlockAll(const GCodeBuffer& gb) noexcept; // Release all locks
+ void UnlockAll(const GCodeBuffer& gb) noexcept; // Release all locks
GCodeBuffer *GetGCodeBuffer(GCodeChannel channel) const noexcept { return gcodeSources[channel.ToBaseType()]; }
#if HAS_MASS_STORAGE
GCodeResult StartSDTiming(GCodeBuffer& gb, const StringRef& reply) noexcept; // Start timing SD card file writing
#endif
- void SavePosition(RestorePoint& rp, const GCodeBuffer& gb) const noexcept; // Save position etc. to a restore point
- void StartToolChange(GCodeBuffer& gb, int toolNum, uint8_t param) noexcept;
+ void SavePosition(const GCodeBuffer& gb, unsigned int restorePointNumber) noexcept
+ pre(restorePointNumber < NumTotalRestorePoints); // Save position etc. to a restore point
+ void StartToolChange(GCodeBuffer& gb, uint8_t param) noexcept;
- unsigned int GetWorkplaceCoordinateSystemNumber() const noexcept { return moveState.currentCoordinateSystem + 1; }
+ unsigned int GetPrimaryWorkplaceCoordinateSystemNumber() const noexcept { return GetPrimaryMovementState().currentCoordinateSystem + 1; }
#if SUPPORT_COORDINATE_ROTATION
void RotateCoordinates(float angleDegrees, float coords[2]) const noexcept; // Account for coordinate rotation
#endif
// This function is called by other functions to account correctly for workplace coordinates
- float GetWorkplaceOffset(size_t axis) const noexcept
+ float GetWorkplaceOffset(const GCodeBuffer& gb, size_t axis) const noexcept
{
- return workplaceCoordinates[moveState.currentCoordinateSystem][axis];
+ return workplaceCoordinates[GetConstMovementState(gb).currentCoordinateSystem][axis];
}
#if SUPPORT_OBJECT_MODEL
@@ -245,6 +246,8 @@ public:
{
return workplaceCoordinates[workplaceNumber][axis];
}
+ float GetPrimaryMaxPrintingAcceleration() const noexcept { return moveStates[0].maxPrintingAcceleration; }
+ float GetPrimaryMaxTravelAcceleration() const noexcept { return moveStates[0].maxTravelAcceleration; }
# if SUPPORT_COORDINATE_ROTATION
float GetRotationAngle() const noexcept { return g68Angle; }
@@ -254,19 +257,25 @@ public:
size_t GetNumInputs() const noexcept { return NumGCodeChannels; }
const GCodeBuffer* GetInput(size_t n) const noexcept { return gcodeSources[n]; }
const GCodeBuffer* GetInput(GCodeChannel n) const noexcept { return gcodeSources[n.RawValue()]; }
+
const ObjectTracker *GetBuildObjects() const noexcept { return &buildObjects; }
- const RestorePoint *GetRestorePoint(size_t n) const pre(n < NumRestorePoints) { return &numberedRestorePoints[n]; }
- float GetVirtualExtruderPosition() const noexcept { return virtualExtruderPosition; }
+
+ const MovementState& GetMovementState(unsigned int queueNumber) pre(queueNumber < NumMovementSystems) const noexcept { return moveStates[queueNumber]; }
+ const MovementState& GetPrimaryMovementState() const noexcept { return moveStates[0]; } // Temporary support for object model and status report values that only handle a single movement system
+ const MovementState& GetCurrentMovementState(const ObjectExplorationContext& context) const noexcept;
+ const MovementState& GetConstMovementState(const GCodeBuffer& gb) const noexcept; // Get a reference to the movement state associated with the specified GCode buffer (there is a private non-const version)
+ bool IsHeaterUsedByDifferentCurrentTool(int heaterNumber, const Tool *tool) const noexcept; // Check if the specified heater is used by a current tool other than the specified one
+ void MessageBoxClosed(bool cancelled, bool m292, ExpressionValue rslt) noexcept;
# if HAS_VOLTAGE_MONITOR
const char *_ecv_array null GetPowerFailScript() const noexcept { return powerFailScript; }
# endif
# if SUPPORT_LASER
- // Return laser PWM in 0..1
+ // Return laser PWM in 0..1. Only the primary movement queue is permitted to control the laser.
float GetLaserPwm() const noexcept
{
- return (float)moveState.laserPwmOrIoBits.laserPwm * (1.0/65535);
+ return (float)moveStates[0].laserPwmOrIoBits.laserPwm * (1.0/65535);
}
# endif
#endif
@@ -300,7 +309,6 @@ public:
static constexpr const char* CANCEL_G = "cancel.g";
static constexpr const char* START_G = "start.g";
static constexpr const char* STOP_G = "stop.g";
- static constexpr const char* SLEEP_G = "sleep.g";
static constexpr const char* CONFIG_OVERRIDE_G = "config-override.g";
static constexpr const char* DefaultHeightMapFile = "heightmap.csv";
static constexpr const char* LOAD_FILAMENT_G = "load.g";
@@ -311,6 +319,9 @@ public:
static constexpr const char* FILAMENT_CHANGE_G = "filament-change.g";
static constexpr const char* DAEMON_G = "daemon.g";
static constexpr const char* RUNONCE_G = "runonce.g";
+#if SUPPORT_PROBE_POINTS_FILE
+ static constexpr const char* DefaultProbeProbePointsFile = "probePoints.csv";
+#endif
private:
GCodes(const GCodes&) = delete;
@@ -318,10 +329,9 @@ private:
// Resources that can be locked.
// To avoid deadlock, if you need multiple resources then you must lock them in increasing numerical order.
typedef uint32_t Resource;
- static const Resource MoveResource = 0; // Movement system, including canned cycle variables
- static const Resource FileSystemResource = 1; // Non-sharable parts of the file system
- static const Resource HeaterResourceBase = 2;
- static const size_t NumResources = HeaterResourceBase + 1;
+ static const Resource MoveResourceBase = 0; // Movement system and associated variables
+ static const Resource FileSystemResource = MoveResourceBase + NumMovementSystems; // Non-sharable parts of the file system
+ static const size_t NumResources = FileSystemResource + 1;
static_assert(NumResources <= sizeof(Resource) * CHAR_BIT, "Too many resources to keep a bitmap of them in class GCodeMachineState");
@@ -333,6 +343,7 @@ private:
bool LockResource(const GCodeBuffer& gb, Resource r) noexcept; // Lock the resource, returning true if success
bool LockFileSystem(const GCodeBuffer& gb) noexcept; // Lock the unshareable parts of the file system
bool LockMovement(const GCodeBuffer& gb) noexcept; // Lock movement
+ bool LockAllMovement(const GCodeBuffer& gb) noexcept; // Lock movement on all queues
void GrabResource(const GCodeBuffer& gb, Resource r) noexcept; // Grab a resource even if it is already owned
void GrabMovement(const GCodeBuffer& gb) noexcept; // Grab the movement lock even if it is already owned
void UnlockResource(const GCodeBuffer& gb, Resource r) noexcept; // Unlock the resource if we own it
@@ -363,14 +374,14 @@ private:
void HandleReply(GCodeBuffer& gb, OutputBuffer *reply) noexcept;
void HandleReplyPreserveResult(GCodeBuffer& gb, GCodeResult rslt, const char *reply) noexcept; // Handle G-Code replies
- GCodeResult TryMacroFile(GCodeBuffer& gb) noexcept; // Try to find a macro file that implements a G or M command
+ GCodeResult TryMacroFile(GCodeBuffer& gb) noexcept; // Try to find a macro file that implements a G or M command
- bool DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& err) THROWS(GCodeException) SPEED_CRITICAL; // Execute a straight move
- bool DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err) THROWS(GCodeException) // Execute an arc move
+ bool DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeException) SPEED_CRITICAL; // Execute a straight move
+ bool DoArcMove(GCodeBuffer& gb, bool clockwise) THROWS(GCodeException) // Execute an arc move
pre(segmentsLeft == 0; resourceOwners[MoveResource] == &gb);
- void FinaliseMove(GCodeBuffer& gb) noexcept; // Adjust the move parameters to account for segmentation and/or part of the move having been done already
- bool CheckEnoughAxesHomed(AxesBitmap axesMoved) noexcept; // Check that enough axes have been homed
- bool TravelToStartPoint(GCodeBuffer& gb) noexcept; // Set up a move to travel to the resume point
+ void FinaliseMove(GCodeBuffer& gb, MovementState& ms) noexcept; // Adjust the move parameters to account for segmentation and/or part of the move having been done already
+ bool CheckEnoughAxesHomed(AxesBitmap axesToMove) noexcept; // Check that enough axes have been homed
+ bool TravelToStartPoint(GCodeBuffer& gb) noexcept; // Set up a move to travel to the resume point
GCodeResult DoDwell(GCodeBuffer& gb) THROWS(GCodeException); // Wait for a bit
GCodeResult DoHome(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Home some axes
@@ -387,6 +398,10 @@ private:
pre(drive < platform.GetNumActualDirectDrivers()); // Deal with M569 for one local driver
GCodeResult ConfigureLocalDriverBasicParameters(GCodeBuffer& gb, const StringRef& reply, uint8_t drive) THROWS(GCodeException)
pre(drive < platform.GetNumActualDirectDrivers()); // Deal with M569.0 for one local driver
+ GCodeResult ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException); // process M204
+ GCodeResult DoMessageBox(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException); // process M291
+ GCodeResult AcknowledgeMessage(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException); // process M292
+
#if SUPPORT_ACCELEROMETERS
GCodeResult ConfigureAccelerometer(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Deal with M955
GCodeResult StartAccelerometer(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Deal with M956
@@ -402,7 +417,7 @@ private:
bool ProcessWholeLineComment(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Process a whole-line comment
- const char *LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, bool isPrintingMove) THROWS(GCodeException); // Set up the extrusion of a move
+ void LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, MovementState& ms, bool isPrintingMove) THROWS(GCodeException); // Set up the extrusion of a move
bool Push(GCodeBuffer& gb, bool withinSameFile) noexcept; // Push feedrate etc on the stack
void Pop(GCodeBuffer& gb, bool withinSameFile) noexcept; // Pop feedrate etc
@@ -420,30 +435,41 @@ private:
ReadLockedPointer<Tool> GetSpecifiedOrCurrentTool(GCodeBuffer& gb) THROWS(GCodeException);
GCodeResult ManageTool(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Create a new tool definition
- void SetToolHeaters(Tool *tool, float temperature, bool both) THROWS(GCodeException); // Set all a tool's heaters to the temperature, for M104/M109
+ void SetToolHeaters(const GCodeBuffer& gb, Tool *tool, float temperature) THROWS(GCodeException); // Set all a tool's heaters active and standby temperatures, for M104/M109
bool ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling, float tolerance) const noexcept;
// Wait for the heaters associated with the specified tool to reach their set temperatures
- void GenerateTemperatureReport(const StringRef& reply) const noexcept; // Store a standard-format temperature report in reply
+ void GenerateTemperatureReport(const GCodeBuffer& gb, const StringRef& reply) const noexcept; // Store a standard-format temperature report in reply
OutputBuffer *GenerateJsonStatusResponse(int type, int seq, ResponseSource source) const noexcept; // Generate a M408 response
void CheckReportDue(GCodeBuffer& gb, const StringRef& reply) const noexcept; // Check whether we need to report temperatures or status
void RestorePosition(const RestorePoint& rp, GCodeBuffer *gb) noexcept; // Restore user position from a restore point
- void UpdateCurrentUserPosition(const GCodeBuffer& gb) noexcept; // Get the current position from the Move class
- void ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes], AxesBitmap explicitAxes = AxesBitmap()) const noexcept;
+ void UpdateCurrentUserPosition(const GCodeBuffer& gb) noexcept; // Get the machine position from the Move class and transform it to the user position
+ void UpdateUserPositionFromMachinePosition(const GCodeBuffer& gb, MovementState& ms) noexcept; // Update the user position from the machine position
+ void ToolOffsetTransform(MovementState& ms, AxesBitmap explicitAxes = AxesBitmap()) const noexcept;
+ // Convert user coordinates to head reference point coordinates
+ void ToolOffsetTransform(const MovementState& ms, const float coordsIn[MaxAxes], float coordsOut[MaxAxes], AxesBitmap explicitAxes = AxesBitmap()) const noexcept;
// Convert user coordinates to head reference point coordinates
- void ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes]) const noexcept; // Convert head reference point coordinates to user coordinates
- float GetCurrentToolOffset(size_t axis) const noexcept; // Get an axis offset of the current tool
+ void ToolOffsetInverseTransform(MovementState& ms) const noexcept; // Convert head reference point coordinates to user coordinates
+ void ToolOffsetInverseTransform(const MovementState& ms, const float coordsIn[MaxAxes], float coordsOut[MaxAxes]) const noexcept;
+ // Convert head reference point coordinates to user coordinates
+ // Tool management
+ void ReportToolTemperatures(const StringRef& reply, const Tool *tool, bool includeNumber) const noexcept;
+
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
+ bool WriteToolSettings(FileStore *f, const MovementState& ms) const noexcept; // save some information for the resume file
+ bool WriteToolParameters(FileStore *f, const bool forceWriteOffsets) const noexcept; // save some information in config-override.g
+#endif
- GCodeResult RetractFilament(GCodeBuffer& gb, bool retract); // Retract or un-retract filaments
+ GCodeResult RetractFilament(GCodeBuffer& gb, bool retract) THROWS(GCodeException); // Retract or un-retract filaments
GCodeResult LoadFilament(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Load the specified filament into a tool
GCodeResult UnloadFilament(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Unload the current filament from a tool
bool ChangeMicrostepping(size_t axisOrExtruder, unsigned int microsteps, bool interp, const StringRef& reply) const noexcept; // Change microstepping on the specified drive
void CheckTriggers() noexcept; // Check for and execute triggers
void DoEmergencyStop() noexcept; // Execute an emergency stop
- void DoPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newState) noexcept // Pause the print
- pre(resourceOwners[movementResource] == &gb);
+ bool DoSynchronousPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newState) noexcept; // Pause the print due to a command in the file itself
+ bool DoAsynchronousPause(GCodeBuffer& gb, PrintPausedReason reason, GCodeState newState) noexcept; // Pause the print returning true if successful, false if we can't yet
void CheckForDeferredPause(GCodeBuffer& gb) noexcept; // Check if a pause is pending, action it if so
void ProcessEvent(GCodeBuffer& gb) noexcept; // Start processing a new event
@@ -451,20 +477,22 @@ private:
bool DoEmergencyPause() noexcept; // Do an emergency pause following loss of power or a motor stall
#endif
- bool IsMappedFan(unsigned int fanNumber) noexcept; // Return true if this fan number is currently being used as a print cooling fan
-
GCodeResult DefineGrid(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException); // Define the probing grid, returning true if error
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
- GCodeResult LoadHeightMap(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Load the height map from file
- bool TrySaveHeightMap(const char *filename, const StringRef& reply) const noexcept; // Save the height map to the specified file
- GCodeResult SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const; // Save the height map to the file specified by P parameter
+ GCodeResult LoadHeightMap(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Load the height map from file
+ bool TrySaveHeightMap(const char *filename, const StringRef& reply) const noexcept; // Save the height map to the specified file
+ GCodeResult SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const THROWS(GCodeException); // Save the height map to the file specified by P parameter
+# if SUPPORT_PROBE_POINTS_FILE
+ GCodeResult LoadProbePointsMap(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Load map of reachable probe points from file
+ void ClearProbePointsInvalid() noexcept;
+# endif
#endif
- void ClearBedMapping(); // Stop using bed compensation
- GCodeResult ProbeGrid(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Start probing the grid, returning true if we didn't because of an error
- ReadLockedPointer<ZProbe> SetZProbeNumber(GCodeBuffer& gb, char probeLetter) THROWS(GCodeException); // Set up currentZProbeNumber and return the probe
- GCodeResult ExecuteG30(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Probes at a given position - see the comment at the head of the function itself
- void InitialiseTaps(bool fastThenSlow) noexcept; // Set up to do the first of a possibly multi-tap probe
- void SetBedEquationWithProbe(int sParam, const StringRef& reply); // Probes a series of points and sets the bed equation
+ void ClearBedMapping() noexcept; // Stop using bed compensation
+ GCodeResult ProbeGrid(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Start probing the grid, returning true if we didn't because of an error
+ ReadLockedPointer<ZProbe> SetZProbeNumber(GCodeBuffer& gb, char probeLetter) THROWS(GCodeException); // Set up currentZProbeNumber and return the probe
+ GCodeResult ExecuteG30(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Probes at a given position - see the comment at the head of the function itself
+ void InitialiseTaps(bool fastThenSlow) noexcept; // Set up to do the first of a possibly multi-tap probe
+ void SetBedEquationWithProbe(int sParam, const StringRef& reply) noexcept; // Probes a series of points and sets the bed equation
GCodeResult ConfigureTrigger(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M581
GCodeResult CheckTrigger(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M582
@@ -473,6 +501,12 @@ private:
GCodeResult WaitForPin(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException); // Handle M577
GCodeResult RaiseEvent(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException); // Handle M957
+ // Object cancellation support
+ GCodeResult HandleM486(GCodeBuffer& gb, const StringRef &reply, OutputBuffer*& buf) THROWS(GCodeException);
+ void StartObject(GCodeBuffer& gb, const char *_ecv_array label) noexcept;
+ void StopObject(GCodeBuffer& gb) noexcept;
+ void ChangeToObject(GCodeBuffer& gb, int i) noexcept;
+
#if HAS_WIFI_NETWORKING || HAS_AUX_DEVICES || HAS_MASS_STORAGE || HAS_SBC_INTERFACE
GCodeResult UpdateFirmware(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException); // Handle M997
#endif
@@ -497,37 +531,43 @@ private:
void AppendAxes(const StringRef& reply, AxesBitmap axes) const noexcept; // Append a list of axes to a string
- void EndSimulation(GCodeBuffer *gb) noexcept; // Restore positions etc. when exiting simulation mode
- bool IsCodeQueueIdle() const noexcept; // Return true if the code queue is idle
+ void EndSimulation(GCodeBuffer *null gb) noexcept; // Restore positions etc. when exiting simulation mode
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
void SaveResumeInfo(bool wasPowerFailure) noexcept;
#endif
- void NewSingleSegmentMoveAvailable() noexcept; // Flag that a new move is available
- void NewMoveAvailable() noexcept; // Flag that a new move is available
+ void NewSingleSegmentMoveAvailable(MovementState& ms) noexcept; // Flag that a new move is available
+ void NewMoveAvailable(MovementState& ms) noexcept; // Flag that a new move is available
- void SetMoveBufferDefaults() noexcept; // Set up default values in the move buffer
+ void SetMoveBufferDefaults(MovementState& ms) noexcept; // Set up default values in the move buffer
void ChangeExtrusionFactor(unsigned int extruder, float factor) noexcept; // Change a live extrusion factor
+ MovementState& GetMovementState(const GCodeBuffer& gb) noexcept; // Get a reference to the movement state associated with the specified GCode buffer
+
+#if SUPPORT_ASYNC_MOVES
+ GCodeResult SelectMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M596
+ GCodeResult CollisionAvoidance(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M597
+ void AllocateAxes(const GCodeBuffer& gb, MovementState& ms, AxesBitmap axes) THROWS(GCodeException); // allocate axes to a movement state
+ bool SyncWith(GCodeBuffer& thisGb, const GCodeBuffer& otherGb) noexcept; // synchronise motion systems
+ void UpdateUserCoordinatesAndReleaseOwnedAxes(GCodeBuffer& thisGb, const GCodeBuffer& otherGb) noexcept;
+#endif
+
#if SUPPORT_COORDINATE_ROTATION
GCodeResult HandleG68(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle G68
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
int GetHeaterNumber(unsigned int itemNumber) const noexcept;
#endif
Pwm_t ConvertLaserPwm(float reqVal) const noexcept;
#if HAS_AUX_DEVICES
-#if !ALLOW_ARBITRARY_PANELDUE_PORT
- static constexpr
-#endif
- uint8_t serialChannelForPanelDueFlashing
-#if !ALLOW_ARBITRARY_PANELDUE_PORT
- = 1
-#endif
- ;
+# if ALLOW_ARBITRARY_PANELDUE_PORT
+ uint8_t serialChannelForPanelDueFlashing;
+# else
+ static constexpr uint8_t serialChannelForPanelDueFlashing = 1;
+# endif
static bool emergencyStopCommanded;
static void CommandEmergencyStop(AsyncSerial *p) noexcept;
#endif
@@ -541,18 +581,22 @@ private:
GCodeBuffer* gcodeSources[NumGCodeChannels]; // The various sources of gcodes
- GCodeBuffer*& httpGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::HTTP)];
- GCodeBuffer*& telnetGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Telnet)];
- GCodeBuffer*& fileGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::File)];
- GCodeBuffer*& usbGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USB)];
- GCodeBuffer*& auxGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux)]; // This one is for the PanelDue on the async serial interface
- GCodeBuffer*& triggerGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Trigger)]; // Used for executing config.g and trigger macro files
- GCodeBuffer*& queuedGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue)];
- GCodeBuffer*& lcdGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::LCD)]; // This one for the 12864 LCD
- GCodeBuffer*& spiGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::SBC)];
- GCodeBuffer*& daemonGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Daemon)];
- GCodeBuffer*& aux2GCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux2)]; // This one is reserved for the second async serial interface
- GCodeBuffer*& autoPauseGCode = gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Autopause)]; // ***THIS ONE MUST BE LAST*** GCode state machine used to run macros on power fail, heater faults and filament out
+ GCodeBuffer* HttpGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::HTTP)]; }
+ GCodeBuffer* TelnetGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Telnet)]; }
+ GCodeBuffer* FileGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::File)]; }
+ GCodeBuffer* UsbGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::USB)]; }
+ GCodeBuffer* AuxGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux)]; } // This one is for the PanelDue on the async serial interface
+ GCodeBuffer* TriggerGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Trigger)]; } // Used for executing config.g and trigger macro files
+ GCodeBuffer* QueuedGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue)]; }
+ GCodeBuffer* LcdGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::LCD)]; } // This one for the 12864 LCD
+ GCodeBuffer* SpiGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::SBC)]; }
+ GCodeBuffer* DaemonGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Daemon)]; }
+ GCodeBuffer* Aux2GCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Aux2)]; } // This one is reserved for the second async serial interface
+ GCodeBuffer* AutoPauseGCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Autopause)]; } // GCode state machine used to run macros on power fail, heater faults and filament out
+#if SUPPORT_ASYNC_MOVES
+ GCodeBuffer* File2GCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::File2)]; }
+ GCodeBuffer* Queue2GCode() const noexcept { return gcodeSources[GCodeChannel::ToBaseType(GCodeChannel::Queue2)]; }
+#endif
size_t nextGcodeSource; // The one to check next, using round-robin scheduling
@@ -567,10 +611,8 @@ private:
MachineType machineType; // whether FFF, laser or CNC
bool active; // Live and running?
- FilePosition printFilePositionAtMacroStart;
const char *_ecv_array null deferredPauseCommandPending;
PauseState pauseState; // whether the machine is running normally or is pausing, paused or resuming
- bool pausedInMacro; // if we are paused then this is true if we paused while fileGCode was executing a macro
bool runningConfigFile; // We are running config.g during the startup process
bool doingToolChange; // We are running tool change macros
@@ -580,28 +622,12 @@ private:
#endif
// The following contain the details of moves that the Move module fetches
- MovementState moveState; // Move details
- GCodeBuffer *null updateUserPositionGb; // if this is non-null then we need to update the user position from he machine position
-
- unsigned int segmentsLeftToStartAt;
- float moveFractionToSkip;
- float firstSegmentFractionToSkip;
-
- float restartMoveFractionDone; // how much of the next move was printed before the pause or power failure (from M26)
- float restartInitialUserC0; // if the print was paused during an arc move, the user X coordinate at the start of that move (from M26)
- float restartInitialUserC1; // if the print was paused during an arc move, the user Y coordinate at the start of that move (from M26)
-
- RestorePoint simulationRestorePoint; // The position and feed rate when we started a simulation
-
- RestorePoint numberedRestorePoints[NumRestorePoints]; // Restore points accessible using the R parameter in the G0/G1 command
- RestorePoint& pauseRestorePoint = numberedRestorePoints[1]; // The position and feed rate when we paused the print
- RestorePoint& toolChangeRestorePoint = numberedRestorePoints[2]; // The position and feed rate when we freed a tool
+ MovementState moveStates[NumMovementSystems]; // Move details
size_t numTotalAxes; // How many axes we have
size_t numVisibleAxes; // How many axes are visible
size_t numExtruders; // How many extruders we have, or may have
float axisScaleFactors[MaxAxes]; // Scale XYZ coordinates by this factor
- float virtualExtruderPosition; // Virtual extruder position of the last move fed into the Move class
float rawExtruderTotalByDrive[MaxExtruders]; // Extrusion amount in the last G1 command with an E parameter when in absolute extrusion mode
float rawExtruderTotal; // Total extrusion amount fed to Move class since starting print, before applying extrusion factor, summed over all drives
@@ -615,13 +641,6 @@ private:
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
FileData fileToPrint; // The next file to print
#endif
-#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
- FilePosition fileOffsetToPrint; // The offset to print from
-#endif
-
- // Tool change. These variables can be global because movement is locked while doing a tool change, so only one can take place at a time.
- int16_t newToolNumber;
- uint8_t toolChangeParam;
char axisLetters[MaxAxes + 1]; // The names of the axes, with a null terminator
bool limitAxes; // Don't think outside the box
@@ -631,9 +650,8 @@ private:
AxesBitmap axesHomed; // Bitmap of which axes have been homed
AxesBitmap axesVirtuallyHomed; // same as axesHomed except all bits are set when simulating
- float lastDefaultFanSpeed; // Last speed given in a M106 command with no fan number
- float speedFactor; // speed factor as a fraction (normally 1.0)
float extrusionFactors[MaxExtruders]; // extrusion factors (normally 1.0)
+ float filamentDiameters[MaxExtruders]; // diameter of the filament in each extruder
float volumetricExtrusionFactors[MaxExtruders]; // Volumetric extrusion factors
float currentBabyStepOffsets[MaxAxes]; // The accumulated axis offsets due to baby stepping requests
@@ -654,6 +672,9 @@ private:
bool zDatumSetByProbing; // true if the Z position was last set by probing, not by an endstop switch or by G92
int8_t tapsDone; // how many times we tapped the current point
uint8_t currentZProbeNumber; // which Z probe a G29 or G30 command is using
+#if SUPPORT_PROBE_POINTS_FILE
+ bool probePointsFileLoaded; // true if we loaded a probe point file since we last cleared the grid
+#endif
// Simulation and print time
float simulationTime; // Accumulated simulation time
@@ -673,9 +694,6 @@ private:
bool isFlashingPanelDue; // Are we in the process of flashing PanelDue?
#endif
- // Code queue
- GCodeQueue *codeQueue; // Stores certain codes for deferred execution
-
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
// SHA1 hashing
FileStore *fileBeingHashed;
@@ -695,6 +713,11 @@ private:
uint32_t lastWarningMillis; // When we last sent a warning message for things that can happen very often
AxesBitmap axesToSenseLength; // The axes on which we are performing axis length sensing
+#if SUPPORT_ASYNC_MOVES
+ AxesBitmap axesAndExtrudersMoved; // axes and extruders that have moved since the last sync
+ CollisionAvoider collisionChecker;
+#endif
+
#if HAS_MASS_STORAGE
static constexpr uint32_t SdTimingByteIncrement = 8 * 1024; // how many timing bytes we write at a time
static constexpr const char *TimingFileName = "test.tst"; // the name of the file we write
@@ -712,7 +735,7 @@ private:
bool isWaiting; // True if waiting to reach temperature
bool cancelWait; // Set true to cancel waiting
bool displayNoToolWarning; // True if we need to display a 'no tool selected' warning
- bool m501SeenInConfigFile; // true if M501 was executed form config.g
+ bool m501SeenInConfigFile; // true if M501 was executed from config.g
bool daemonRunning;
char filamentToLoad[FilamentNameLength]; // Name of the filament being loaded
@@ -727,6 +750,38 @@ inline float GCodes::GetTotalBabyStepOffset(size_t axis) const noexcept
return currentBabyStepOffsets[axis];
}
+#if SUPPORT_ASYNC_MOVES
+
+inline bool GCodes::LockMovementAndWaitForStandstillNoSync(GCodeBuffer& gb) noexcept
+{
+ return LockMovementAndWaitForStandstill(gb, false);
+}
+
+#else
+
+inline bool GCodes::LockMovementAndWaitForStandstillNoSync(GCodeBuffer& gb) noexcept
+{
+ return LockMovementAndWaitForStandstill(gb);
+}
+
+// Get a reference to the movement state associated with the specified GCode buffer
+inline MovementState& GCodes::GetMovementState(const GCodeBuffer& gb) noexcept
+{
+ return moveStates[0];
+}
+
+inline const MovementState& GCodes::GetConstMovementState(const GCodeBuffer& gb) const noexcept
+{
+ return moveStates[0];
+}
+
+inline const MovementState& GCodes::GetCurrentMovementState(const ObjectExplorationContext& context) const noexcept
+{
+ return moveStates[0];
+}
+
+#endif
+
//*****************************************************************************************************
#endif
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index 68ea6823..b9aa5be3 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -37,7 +37,7 @@
# include <Comms/FirmwareUpdater.h>
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
# include <Display/Display.h>
#endif
@@ -68,25 +68,6 @@ bool GCodes::ActOnCode(GCodeBuffer& gb, const StringRef& reply) noexcept
{
try
{
- // Can we queue this code?
- if (gb.CanQueueCodes() && codeQueue->ShouldQueueCode(gb))
- {
- // Don't queue any GCodes if there are segments not yet picked up by Move, because in the event that a segment corresponds to no movement,
- // the move gets discarded, which throws out the count of scheduled moves and hence the synchronisation
- if (moveState.segmentsLeft != 0)
- {
- return false;
- }
-
- if (codeQueue->QueueCode(gb, reprap.GetMove().GetScheduledMoves() + moveState.segmentsLeft))
- {
- HandleReply(gb, GCodeResult::ok, "");
- return true;
- }
-
- return false; // we should queue this code but we can't, so wait until we can either execute it or queue it
- }
-
switch (gb.GetCommandLetter())
{
case 'G':
@@ -127,29 +108,53 @@ bool GCodes::ActOnCode(GCodeBuffer& gb, const StringRef& reply) noexcept
return true;
}
+// Handle G-command returning true if the command completed, false if this function needs to be called again to complete it
bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
GCodeResult result = GCodeResult::ok;
const int code = gb.GetCommandNumber();
- if (IsSimulating() && code > 4 && code != 10 && code != 11 && code != 20 && code != 21 && (code < 53 || code > 59) && (code < 90 || code > 92))
+ if (IsSimulating() && code > 4 && code != 10 && code != 11 && code != 20 && code != 21 && (code < 53 || code > 59) && (code < 90 || code > 94))
{
HandleReply(gb, result, "");
return true; // we only simulate some gcodes
}
+ // The only queued GCodes are some subfunctions of G10, so we delay checking for queueing this command until we are in case 10 below
+
+#if SUPPORT_ASYNC_MOVES
+ const bool executing = gb.Executing(); // this is used by the BREAK_IF_NOT_PRIMARY macro and elsewhere
+#endif
if (gb.GetCommandFraction() > 0
&& code != 38 && code != 59 // these are the only G-codes we implement that can have fractional parts
)
{
+#if SUPPORT_ASYNC_MOVES
+ if (executing)
+ {
+ result = TryMacroFile(gb);
+ }
+ else
+ {
+ HandleReply(gb, result, "");
+ return true;
+ }
+#else
result = TryMacroFile(gb);
+#endif
}
else
{
+#if SUPPORT_ASYNC_MOVES
+# define BREAK_IF_NOT_EXECUTING if (!executing) { break; }
+#else
+# define BREAK_IF_NOT_EXECUTING // nothing
+#endif
switch (code)
{
case 0: // Rapid move
case 1: // Ordinary move
- if (moveState.segmentsLeft != 0) // do this check first to avoid locking movement unnecessarily
+ BREAK_IF_NOT_EXECUTING
+ if (GetMovementState(gb).segmentsLeft != 0) // do this check first to avoid locking movement unnecessarily
{
return false;
}
@@ -157,24 +162,25 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
return false;
}
+ try
{
- const char* err = nullptr;
- if (!DoStraightMove(gb, code == 1, err))
+ if (!DoStraightMove(gb, code == 1))
{
return false;
}
- if (err != nullptr)
- {
- gb.SetState(GCodeState::abortWhenMovementFinished); // empty the queue before ending simulation, and force the user position to be restored
- gb.LatestMachineState().SetError(err); // must do this *after* calling SetState
- }
+ } catch (const GCodeException& exc)
+ {
+ platform.GetEndstops().ClearEndstops(); // DoStraightMove may have enabled endstops before quitting, so disable them
+ gb.SetState(GCodeState::abortWhenMovementFinished); // empty the queue before ending simulation, and force the user position to be restored
+ gb.LatestMachineState().SetError(exc); // must do this *after* calling SetState
}
break;
case 2: // Clockwise arc
case 3: // Anti clockwise arc
// We only support X and Y axes in these (and optionally Z for corkscrew moves), but you can map them to other axes in the tool definitions
- if (moveState.segmentsLeft != 0) // do this check first to avoid locking movement unnecessarily
+ BREAK_IF_NOT_EXECUTING
+ if (GetMovementState(gb).segmentsLeft != 0) // do this check first to avoid locking movement unnecessarily
{
return false;
}
@@ -182,17 +188,16 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
return false;
}
+ try
{
- const char* err = nullptr;
- if (!DoArcMove(gb, code == 2, err))
+ if (!DoArcMove(gb, code == 2))
{
return false;
}
- if (err != nullptr)
- {
- gb.SetState(GCodeState::abortWhenMovementFinished); // empty the queue before ending simulation, and force the user position to be restored
- gb.LatestMachineState().SetError(err); // must do this *after* calling SetState
- }
+ } catch (GCodeException& exc)
+ {
+ gb.SetState(GCodeState::abortWhenMovementFinished); // empty the queue before ending simulation, and force the user position to be restored
+ gb.LatestMachineState().SetError(exc); // must do this *after* calling SetState
}
break;
@@ -209,6 +214,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
switch (ival)
{
case 1:
+ BREAK_IF_NOT_EXECUTING
result = SetOrReportOffsets(gb, reply, 10); // same as G10 with offsets and no L parameter
break;
@@ -228,7 +234,8 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
else
#endif
{
- bool modifyingTool = gb.Seen('P') || gb.Seen('R') || gb.Seen('S') || gb.Seen('F');
+ BREAK_IF_NOT_EXECUTING
+ bool modifyingTool = gb.Seen('P') || gb.Seen('R') || gb.Seen('S');
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
modifyingTool |= gb.Seen(axisLetters[axis]);
@@ -240,6 +247,26 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
break;
}
+
+ // Should we queue this code?
+ if (gb.CanQueueCodes())
+ {
+ GCodeQueue * const codeQueue = GetMovementState(gb).codeQueue;
+ if (codeQueue->ShouldQueueG10(gb))
+ {
+ // Don't queue any GCodes if there are segments not yet picked up by Move, because in the event that a segment corresponds to no movement,
+ // the move gets discarded, which throws out the count of scheduled moves and hence the synchronisation
+ if (GetMovementState(gb).segmentsLeft == 0 && codeQueue->QueueCode(gb))
+ {
+ HandleReply(gb, GCodeResult::ok, "");
+ return true;
+ }
+
+ return false; // we should queue this code but we can't yet, so wait until we can either execute it or queue it
+ }
+ }
+
+ // We don't want to queue it
result = SetOrReportOffsets(gb, reply, 10);
}
else
@@ -251,6 +278,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 11: // Un-retract
+ BREAK_IF_NOT_EXECUTING
result = RetractFilament(gb, false);
break;
@@ -263,19 +291,21 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
gb.LatestMachineState().selectedPlane = code - 17;
- break;
-
- case 20: // Inches (which century are we living in, here?)
- gb.UseInches(true);
reprap.InputsUpdated();
break;
+ case 20: // Inches (which century are we living in, here?)
case 21: // mm
- gb.UseInches(false);
+ gb.UseInches(code == 20);
reprap.InputsUpdated();
break;
case 28: // Home
+ if (!LockMovementAndWaitForStandstill(gb))
+ {
+ return false;
+ }
+ BREAK_IF_NOT_EXECUTING
result = DoHome(gb, reply);
break;
@@ -284,6 +314,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
return false;
}
+ BREAK_IF_NOT_EXECUTING
{
int sparam;
if (gb.Seen('S'))
@@ -315,6 +346,9 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
case 2: // clear height map
ClearBedMapping();
+#if SUPPORT_PROBE_POINTS_FILE
+ ClearProbePointsInvalid();
+#endif
break;
case 3: // save height map to names file
@@ -325,6 +359,14 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
#endif
break;
+ case 4: // read grid parameters and can/cannot probe map
+#if (HAS_MASS_STORAGE || HAS_SBC_INTERFACE) && SUPPORT_PROBE_POINTS_FILE
+ result = LoadProbePointsMap(gb, reply);
+#else
+ result = GCodeResult::errorNotSupported;
+#endif
+ break;
+
default:
result = GCodeResult::badOrMissingParameter;
break;
@@ -338,6 +380,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
return false;
}
+ BREAK_IF_NOT_EXECUTING
if (reprap.GetMove().GetKinematics().AxesToHomeBeforeProbing().Intersects(~axesVirtuallyHomed))
{
reply.copy("Insufficient axes homed for bed probing");
@@ -350,6 +393,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 31: // Return the probe value, or set probe variables
+ BREAK_IF_NOT_EXECUTING
result = platform.GetEndstops().HandleG31(gb, reply);
break;
@@ -358,6 +402,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
return false;
}
+ BREAK_IF_NOT_EXECUTING
// We need to unlock the movement system here in case there is no Z probe and we are doing manual probing.
// Otherwise, even though the bed probing code calls UnlockAll when doing a manual bed probe, the movement system
@@ -373,10 +418,12 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
return false;
}
+ BREAK_IF_NOT_EXECUTING
result = StraightProbe(gb, reply);
break;
case 53: // Temporarily use machine coordinates
+ BREAK_IF_NOT_EXECUTING
gb.LatestMachineState().g53Active = true;
break;
@@ -399,7 +446,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
if (cs < NumCoordinateSystems)
{
- moveState.currentCoordinateSystem = cs; // this is the zero-base coordinate system number
+ GetMovementState(gb).currentCoordinateSystem = cs; // this is the zero-base coordinate system number
reprap.MoveUpdated();
gb.LatestMachineState().g53Active = false; // cancel any active G53
}
@@ -412,6 +459,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
#endif
case 60: // Save position
+ BREAK_IF_NOT_EXECUTING
result = SavePosition(gb, reply);
break;
@@ -421,7 +469,17 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 69:
- g68Angle = 0.0;
+ if (!LockMovementAndWaitForStandstill(gb))
+ {
+ return false;
+ }
+# if SUPPORT_ASYNC_MOVES
+ if (gb.Executing())
+# endif
+ {
+ g68Angle = 0.0;
+ UpdateCurrentUserPosition(gb);
+ }
break;
#endif
@@ -436,13 +494,25 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 92: // Set position
+ BREAK_IF_NOT_EXECUTING
result = SetPositions(gb, reply);
break;
+ case 93: // inverse time mode
+ gb.LatestMachineState().inverseTimeMode = true;
+ reprap.InputsUpdated();
+ break;
+
+ case 94: //normal feed rate mode
+ gb.LatestMachineState().inverseTimeMode = false;
+ reprap.InputsUpdated();
+ break;
+
default:
result = TryMacroFile(gb);
break;
}
+#undef BREAK_IF_NOT_EXECUTING
}
return HandleResult(gb, result, reply, nullptr);
@@ -452,6 +522,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
const int code = gb.GetCommandNumber();
+ // In simulation mode we don't execute most M-commands
if ( IsSimulating()
&& (code < 20 || code > 37)
&& code != 0 && code != 1 && code != 82 && code != 83 && code != 105 && code != 109 && code != 111 && code != 112 && code != 122
@@ -461,6 +532,81 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
return true; // we don't simulate most M codes
}
+#if SUPPORT_ASYNC_MOVES
+ // If we are not the primary GCode reader for this stream, we don't execute most M-commands
+ if (!gb.Executing())
+ {
+ switch (code)
+ {
+ case 0:
+ case 1:
+ case 23:
+ case 24:
+ case 32:
+ case 37:
+ case 82:
+ case 83:
+ case 98:
+ case 99:
+ case 112:
+ case 120:
+ case 121:
+ case 400:
+ case 555:
+ case 596:
+ // These commands are executed by all GCode processors, at least to start with
+ break;
+
+ case 17:
+ case 18:
+ case 81:
+ case 84:
+ case 190:
+ case 191:
+ case 206:
+ case 375:
+ case 451:
+ case 452:
+ case 453:
+ case 561:
+ case 574:
+ case 665:
+ case 666:
+ case 669:
+ case 671:
+ case 918:
+ // These commands cause synchronisation but are then executed by just the primary processor. The code to implement the command also calls LockMovementAndWaitForStandstill.
+ if (!LockMovementAndWaitForStandstill(gb))
+ {
+ return false;
+ }
+ // no break
+ default:
+ // All remaining commands are executed by the primary processor only
+ HandleReply(gb, GCodeResult::ok, "");
+ return true; // we don't simulate most M codes
+ }
+ }
+#endif
+
+ // Can we queue this code?
+ if (gb.CanQueueCodes())
+ {
+ GCodeQueue * const codeQueue = GetMovementState(gb).codeQueue;
+ if (codeQueue->ShouldQueueMCode(gb))
+ {
+ // Don't queue any GCodes if there are segments not yet picked up by Move, because in the event that a segment corresponds to no movement,
+ // the move gets discarded, which throws out the count of scheduled moves and hence the synchronisation
+ if (GetMovementState(gb).segmentsLeft == 0 && codeQueue->QueueCode(gb))
+ {
+ HandleReply(gb, GCodeResult::ok, "");
+ return true;
+ }
+
+ return false; // we should queue this code but we can't yet, so wait until we can either execute it or queue it
+ }
+ }
+
#if HAS_SBC_INTERFACE
// Pass file- and system-related commands to the SBC service if they came from somewhere else.
// They will be passed back to us via a binary buffer or separate SPI message if necessary.
@@ -496,7 +642,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
GCodeResult result;
if (gb.GetCommandFraction() > 0
- && code != 36 && code != 201 && code != 569 // these are the only M-codes we implement that can have fractional parts
+ && code != 36 && code != 201 && code != 569 && code != 587 // these are the only M-codes we implement that can have fractional parts
)
{
result = TryMacroFile(gb);
@@ -508,153 +654,164 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
case 0: // Stop
case 1: // Sleep
- // Don't allow M0 or M1 to stop a print, unless the print is paused or the command comes from the file being printed itself.
- if (reprap.GetPrintMonitor().IsPrinting() && &gb != fileGCode && pauseState != PauseState::paused)
+ case 2: // Stop
+ if (gb.IsFileChannel())
{
- reply.copy("Pause the print before attempting to cancel it");
- result = GCodeResult::error;
- }
- else if (!LockMovementAndWaitForStandstill(gb)) // wait until everything has stopped and deferred command queue has caught up
- {
- return false;
+ // Stopping a job because of a command in the file
+ if (!LockMovementAndWaitForStandstill(gb)) // wait until everything has stopped and deferred command queue has caught up
+ {
+ return false;
+ }
+ if (&gb == FileGCode())
+ {
+ isWaiting = cancelWait = false; // we may have been waiting for temperatures to be reached
+ StopPrint(StopPrintReason::normalCompletion);
+ }
}
- else
+ else if (pauseState == PauseState::paused)
{
- const auto oldPauseState = pauseState; // pauseState gets reset by CancelPrint
- const bool wasSimulating = IsSimulating(); // simulationMode may get cleared by CancelPrint
+ // Cancelling a print that has been paused
+ if (!LockMovementAndWaitForStandstill(gb)) // make sure everything has stopped
+ {
+ return false;
+ }
isWaiting = cancelWait = false; // we may have been waiting for temperatures to be reached
- StopPrint((&gb == fileGCode) ? StopPrintReason::normalCompletion : StopPrintReason::userCancelled);
-
+ const bool wasSimulating = IsSimulating(); // simulationMode may get cleared by CancelPrint
+ StopPrint(StopPrintReason::userCancelled);
if (!wasSimulating) // don't run any macro files or turn heaters off etc. if we were simulating before we stopped the print
{
- // If we are cancelling a paused print with M0 and we are homed and cancel.g exists then run it and do nothing else
- if (oldPauseState != PauseState::notPaused && code == 0 && AllAxesAreHomed())
+ // If cancel.g exists then run it and do nothing else
+ gb.SetState(GCodeState::cancelling);
+ if (DoFileMacro(gb, CANCEL_G, false, SystemHelperMacroCode))
{
- gb.SetState(GCodeState::cancelling);
- if (DoFileMacro(gb, CANCEL_G, false, SystemHelperMacroCode))
- {
- pauseState = PauseState::cancelling;
- break;
- }
- // The state will be changed a few lines down, so no need to reset it to normal here
+ pauseState = PauseState::cancelling;
+ break;
}
-
- gb.SetState(GCodeState::stopping);
- if (!DoFileMacro(gb, (code == 0) ? STOP_G : SLEEP_G, false, SystemHelperMacroCode))
+ if (!DoFileMacro(gb, STOP_G, false, SystemHelperMacroCode))
{
reprap.GetHeat().SwitchOffAll(true);
}
}
}
+ else
+ {
+ reply.copy("Pause the print before attempting to cancel it");
+ result = GCodeResult::error;
+ }
break;
case 3: // Spin spindle clockwise
case 4: // Spin spindle counter clockwise
- if (machineType == MachineType::cnc)
{
- // Determine what spindle number we are using
- Tool * const currentTool = reprap.GetCurrentTool();
- uint32_t slot;
- if (gb.Seen('P'))
- {
- slot = gb.GetLimitedUIValue('P', MaxSpindles);
- }
- else if (currentTool != nullptr && currentTool->GetSpindleNumber() >= 0)
- {
- slot = currentTool->GetSpindleNumber();
- }
- else
- {
- reply.copy("No P parameter and no active tool with spindle");
- result = GCodeResult::error;
- break;
- }
-
- Spindle& spindle = platform.AccessSpindle(slot);
- if (gb.Seen('S'))
+ MovementState& ms = GetMovementState(gb);
+ if (machineType == MachineType::cnc)
{
- const uint32_t rpm = gb.GetUIValue();
- if (currentTool != nullptr && currentTool->GetSpindleNumber() == (int)slot)
+ // Determine what spindle number we are using
+ uint32_t slot;
+ if (gb.Seen('P'))
+ {
+ slot = gb.GetLimitedUIValue('P', MaxSpindles);
+ }
+ else if (ms.currentTool != nullptr && ms.currentTool->GetSpindleNumber() >= 0)
{
- currentTool->SetSpindleRpm(rpm);
+ slot = ms.currentTool->GetSpindleNumber();
}
else
{
- spindle.SetConfiguredRpm(rpm, false);
+ reply.copy("No P parameter and no active tool with spindle");
+ result = GCodeResult::error;
+ break;
}
+
+ Spindle& spindle = platform.AccessSpindle(slot);
+ if (gb.Seen('S'))
+ {
+ const uint32_t rpm = gb.GetUIValue();
+ if (ms.currentTool != nullptr && ms.currentTool->GetSpindleNumber() == (int)slot)
+ {
+ ms.currentTool->SetSpindleRpm(rpm, true);
+ }
+ else
+ {
+ spindle.SetConfiguredRpm(rpm, false);
+ }
+ }
+ spindle.SetState((code == 4) ? SpindleState::reverse : SpindleState::forward);
}
- spindle.SetState((code == 4) ? SpindleState::reverse : SpindleState::forward);
- }
#if SUPPORT_LASER
- else if (machineType == MachineType::laser && code == 3 && gb.Seen('S'))
- {
- if (moveState.segmentsLeft != 0)
+ else if (machineType == MachineType::laser && code == 3 && gb.Seen('S'))
+ {
+ if (ms.segmentsLeft != 0)
{
return false; // don't modify moves that haven't gone yet
}
- moveState.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
- }
+ ms.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetNonNegativeFValue());
+ }
#endif
- else
- {
- result = GCodeResult::notSupportedInCurrentMode;
+ else
+ {
+ result = GCodeResult::notSupportedInCurrentMode;
+ }
}
break;
case 5: // Spindle motor off
- switch (machineType)
{
- case MachineType::cnc:
+ MovementState& ms = GetMovementState(gb);
+ switch (machineType)
{
- // Determine what spindle number we are using
- Tool * const currentTool = reprap.GetCurrentTool();
- uint32_t slot;
- if (gb.Seen('P'))
- {
- slot = gb.GetLimitedUIValue('P', MaxSpindles);
- }
- else if (currentTool != nullptr && currentTool->GetSpindleNumber() >= 0)
- {
- slot = currentTool->GetSpindleNumber();
- }
- else
+ case MachineType::cnc:
{
- // Turn off every spindle if no 'P' parameter is present and the current tool does not have a spindle
- for (size_t i = 0; i < MaxSpindles; i++)
+ // Determine what spindle number we are using
+ uint32_t slot;
+ if (gb.Seen('P'))
{
- platform.AccessSpindle(i).SetState(SpindleState::stopped);
+ slot = gb.GetLimitedUIValue('P', MaxSpindles);
+ }
+ else if (ms.currentTool != nullptr && ms.currentTool->GetSpindleNumber() >= 0)
+ {
+ slot = ms.currentTool->GetSpindleNumber();
+ }
+ else
+ {
+ // Turn off every spindle if no 'P' parameter is present and the current tool does not have a spindle
+ for (size_t i = 0; i < MaxSpindles; i++)
+ {
+ platform.AccessSpindle(i).SetState(SpindleState::stopped);
+ }
+ break;
}
- break;
- }
- platform.AccessSpindle(slot).SetState(SpindleState::stopped);
- }
- break;
+ platform.AccessSpindle(slot).SetState(SpindleState::stopped);
+ }
+ break;
#if SUPPORT_LASER
- case MachineType::laser:
- if (moveState.segmentsLeft != 0)
- {
- return false; // don't modify moves that haven't gone yet
- }
- moveState.laserPwmOrIoBits.Clear();
- break;
+ case MachineType::laser:
+ {
+ if (ms.segmentsLeft != 0)
+ {
+ return false; // don't modify moves that haven't gone yet
+ }
+ ms.laserPwmOrIoBits.Clear();
+ }
+ break;
#endif
- default:
- result = GCodeResult::notSupportedInCurrentMode;
- break;
+ default:
+ result = GCodeResult::notSupportedInCurrentMode;
+ break;
+ }
}
break;
+ case 17: // Motors on
case 18: // Motors off
case 84:
if (!LockMovementAndWaitForStandstill(gb))
{
return false;
}
- // no break
- case 17: // Motors on
{
bool seen = false;
for (size_t axis = 0; axis < numTotalAxes; axis++)
@@ -702,17 +859,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen('S'))
{
seen = true;
-
- const float idleTimeout = gb.GetFValue();
- if (idleTimeout < 0.0)
- {
- reply.copy("Idle timeouts cannot be negative");
- result = GCodeResult::error;
- }
- else
- {
- reprap.GetMove().SetIdleTimeout(idleTimeout);
- }
+ reprap.GetMove().SetIdleTimeout(gb.GetPositiveFValue());
}
if (!seen)
@@ -841,7 +988,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
case 23: // Set file to print
case 32: // Select file and start SD print
// We now allow a file that is being printed to chain to another file. This is required for the resume-after-power-fail functionality.
- if (fileGCode->IsDoingFile() && (&gb) != fileGCode)
+ if (FileGCode()->IsDoingFile() && !gb.IsFileChannel())
{
reply.copy("Cannot set file to print, because a file is already being printed");
result = GCodeResult::error;
@@ -948,21 +1095,23 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
else
# endif
{
- bool fromStart = (fileOffsetToPrint == 0);
+ const bool fromStart = (moveStates[0].fileOffsetToPrint == 0)
+# if SUPPORT_ASYNC_MOVES
+ && (moveStates[1].fileOffsetToPrint == 0)
+# endif
+ ;
if (!fromStart)
{
// We executed M26 to set the file offset, which normally means that we are executing resurrect.g.
// We need to copy the absolute/relative and volumetric extrusion flags over
- fileGCode->OriginalMachineState().CopyStateFrom(gb.LatestMachineState());
-# if HAS_SBC_INTERFACE
- if (!reprap.UsingSbcInterface())
+ FileGCode()->OriginalMachineState().CopyStateFrom(gb.LatestMachineState());
+# if SUPPORT_ASYNC_MOVES
+ File2GCode()->OriginalMachineState().CopyStateFrom(gb.LatestMachineState());
# endif
-# if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES
+ for (MovementState& ms : moveStates)
{
- fileToPrint.Seek(fileOffsetToPrint);
+ ms.moveFractionToSkip = ms.restartMoveFractionDone;
}
-# endif
- moveFractionToSkip = restartMoveFractionDone;
}
StartPrinting(fromStart);
}
@@ -972,45 +1121,33 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 226: // Synchronous pause, normally initiated from within the file being printed
- case 601:
- if (pauseState == PauseState::notPaused)
+ case 600: // Filament change pause, synchronous
+ case 601: // Pause, used on Prusa printers
+ if (!gb.IsFileChannel())
{
- if (gb.IsDoingFileMacro() && !gb.LatestMachineState().CanRestartMacro())
- {
- if (deferredPauseCommandPending == nullptr) // filament change pause takes priority
- {
- deferredPauseCommandPending = (gb.Seen('P') && gb.GetUIValue() == 0) ? "M226 P0" : "M226";
- }
- }
- else
- {
- if (!LockMovementAndWaitForStandstill(gb)) // lock movement before calling DoPause, also wait for movement to complete
- {
- return false;
- }
- DoPause(gb, PrintPausedReason::gcode, (gb.Seen('P') && gb.GetUIValue() == 0) ? GCodeState::pausing2 : GCodeState::pausing1);
- }
+ reply.copy("use M226 and M601 only within a file being printed"); //TODO handle streaming over USB too
+ result = GCodeResult::error;
}
- break;
-
- case 600: // Filament change pause, synchronous
- if (pauseState == PauseState::notPaused)
+ else if (pauseState == PauseState::notPaused)
{
- if (fileGCode->IsDoingFileMacro())
+ if (gb.IsDoingFileMacro() && !gb.LatestMachineState().CanRestartMacro())
{
- deferredPauseCommandPending = "M600";
- if (&gb != fileGCode)
+ if (deferredPauseCommandPending == nullptr) // filament change pause takes priority
{
- return false; // wait for the current macro to finish
+ deferredPauseCommandPending = (code == 600) ? "M600"
+ : (code == 601) ? "M601"
+ : (gb.Seen('P') && gb.GetUIValue() == 0) ? "M226 P0"
+ : "M226";
}
}
- else
+ else if (!DoSynchronousPause(gb,
+ (code == 600) ? PrintPausedReason::filamentChange
+ : PrintPausedReason::gcode,
+ (code == 600) ? GCodeState::filamentChangePause1
+ : (code == 226 && gb.Seen('P') && gb.GetUIValue() == 0) ? GCodeState::pausing2
+ : GCodeState::pausing1))
{
- if (!LockMovementAndWaitForStandstill(gb)) // lock movement before calling DoPause, also wait for movement to complete
- {
- return false;
- }
- DoPause(gb, PrintPausedReason::filamentChange, GCodeState::filamentChangePause1);
+ return false;
}
}
break;
@@ -1026,39 +1163,43 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
reply.copy("Cannot pause print, because no file is being printed!");
result = GCodeResult::error;
}
- else if (fileGCode->IsDoingFileMacro() && !fileGCode->LatestMachineState().CanRestartMacro())
+ else if (gb.IsFileChannel())
+ {
+ if (!DoSynchronousPause(gb, PrintPausedReason::user, GCodeState::pausing1))
+ {
+ return false;
+ }
+ }
+ else if (FileGCode()->IsDoingFileMacro() && !FileGCode()->LatestMachineState().CanRestartMacro())
{
if (deferredPauseCommandPending == nullptr) // filament change pause takes priority
{
deferredPauseCommandPending = (gb.Seen('P') && gb.GetUIValue() == 0) ? "M226 P0" : "M226";
}
- if (&gb != fileGCode)
+ if (!gb.IsFileChannel())
{
return false; // wait for the current macro to finish
}
}
- else
+ else if (!DoAsynchronousPause(gb, PrintPausedReason::user, GCodeState::pausing1))
{
- if (!LockMovement(gb)) // lock movement before calling DoPause
- {
- return false;
- }
- DoPause(gb, PrintPausedReason::user, GCodeState::pausing1);
+ return false;
}
break;
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
case 26: // Set SD position
- // This is used between executing M23 to set up the file to print, and M25 to print it
+ // This is used between executing M23 to set up the file to print, and M24 to print it
gb.MustSee('S');
- fileOffsetToPrint = (FilePosition)gb.GetUIValue();
- restartMoveFractionDone = (gb.Seen('P')) ? constrain<float>(gb.GetFValue(), 0.0, 1.0) : 0.0;
{
+ MovementState& ms = GetMovementState(gb);
+ ms.fileOffsetToPrint = (FilePosition)gb.GetUIValue();
+ ms.restartMoveFractionDone = (gb.Seen('P')) ? constrain<float>(gb.GetFValue(), 0.0, 1.0) : 0.0;
const unsigned int selectedPlane = gb.LatestMachineState().selectedPlane;
const char c0 = (selectedPlane == 2) ? 'Y' : 'X';
const char c1 = (selectedPlane == 0) ? 'Y' : 'Z';
- restartInitialUserC0 = (gb.Seen(c0)) ? gb.GetFValue() : 0.0;
- restartInitialUserC1 = (gb.Seen(c1)) ? gb.GetFValue() : 0.0;
+ ms.restartInitialUserC0 = (gb.Seen(c0)) ? gb.GetFValue() : 0.0;
+ ms.restartInitialUserC1 = (gb.Seen(c1)) ? gb.GetFValue() : 0.0;
}
break;
#endif
@@ -1068,11 +1209,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (reprap.GetPrintMonitor().IsPrinting())
{
// Pronterface keeps sending M27 commands if "Monitor status" is checked, and it specifically expects the following response syntax
- FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
+ FileData& fileBeingPrinted = FileGCode()->OriginalMachineState().fileState;
// In case there are short periods of time when PrintMonitor says a file is printing but the file is not open, or DSF passes M27 to us, check that we have a file
if (fileBeingPrinted.IsLive())
{
- reply.printf("SD printing byte %lu/%lu", GetFilePosition(), fileBeingPrinted.Length());
+ reply.printf("SD printing byte %lu/%lu", GetPrintingFilePosition(), fileBeingPrinted.Length());
break;
}
}
@@ -1141,7 +1282,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
#if HAS_MASS_STORAGE
- case 1: // get thumbnail
+ case 1: // get thumbnail
{
String<MaxFilenameLength> filename;
gb.MustSee('P');
@@ -1337,11 +1478,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
if (gb.Seen(axisLetters[axis]))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
- platform.SetDriveStepsPerUnit(axis, gb.GetFValue(), ustepMultiplier);
+ platform.SetDriveStepsPerUnit(axis, gb.GetPositiveFValue(), ustepMultiplier);
#if SUPPORT_CAN_EXPANSION
axesToUpdate.SetBit(axis);
#endif
@@ -1351,7 +1492,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen(extrudeLetter))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -1371,10 +1512,19 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
}
+#if SUPPORT_ASYNC_MOVES
+ if (!gb.Executing())
+ {
+ break;
+ }
+#endif
if (seen)
{
// On a delta, if we change the drive steps/mm then we need to recalculate the motor positions
- reprap.GetMove().SetNewPosition(moveState.coords, true);
+ for (size_t i = 0; i < NumMovementSystems; ++i)
+ {
+ reprap.GetMove().SetNewPosition(moveStates[i].coords, true, i);
+ }
#if SUPPORT_CAN_EXPANSION
result = platform.UpdateRemoteStepsPerMmAndMicrostepping(axesToUpdate, reply);
#endif
@@ -1442,7 +1592,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// For case 104, see 109
case 105: // Get temperatures
- GenerateTemperatureReport(reply);
+ GenerateTemperatureReport(gb, reply);
break;
case 106: // Set/report fan values
@@ -1473,30 +1623,34 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (seenFanNum)
{
result = reprap.GetFansManager().SetFanValue(fanNum, f, reply);
- if (IsMappedFan(fanNum))
+ // If this is a print cooling fan for an active tool, set the virtual fan speed in the corresponding MovementState
+ for (MovementState& ms : moveStates)
{
- lastDefaultFanSpeed = f;
+ if (ms.currentTool != nullptr && ms.currentTool->GetFanMapping().IsBitSet(fanNum))
+ {
+ ms.virtualFanSpeed = f;
+ }
}
}
else
{
// We are processing an M106 S### command with no other recognised parameters and we have a tool selected.
// Apply the fan speed setting to the fans in the fan mapping for the current tool.
- SetMappedFanSpeed(f);
+ SetMappedFanSpeed(&gb, f);
}
}
// ConfigureFan doesn't process R parameters
if (gb.Seen('R') && !seenFanNum)
{
- const size_t restorePointNumber = gb.GetLimitedUIValue('R', NumRestorePoints);
- SetMappedFanSpeed(numberedRestorePoints[restorePointNumber].fanSpeed);
+ const size_t restorePointNumber = gb.GetLimitedUIValue('R', NumVisibleRestorePoints);
+ SetMappedFanSpeed(&gb, GetMovementState(gb).restorePoints[restorePointNumber].fanSpeed);
}
}
break;
case 107: // Fan off - deprecated
- SetMappedFanSpeed(0.0);
+ SetMappedFanSpeed(&gb, 0.0);
break;
case 108: // Cancel waiting for temperature
@@ -1508,7 +1662,13 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
case 109: // Deprecated in RRF, but widely generated by slicers
{
- const bool movementWasLocked = gb.LatestMachineState().lockedResources.IsBitSet(MoveResource);
+ const bool movementWasLocked = gb.LatestMachineState().lockedResources.IsBitSet(
+#if SUPPORT_ASYNC_MOVES
+ MoveResourceBase + gb.GetQueueNumberToLock()
+#else
+ MoveResourceBase
+#endif
+ );
if (!LockMovementAndWaitForStandstill(gb)) // wait until movement has finished and deferred command queue has caught up to avoid out-of-order execution
{
return false;
@@ -1556,7 +1716,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
int32_t toolNumber = 0;
bool seenT = false;
gb.TryGetIValue('T', toolNumber, seenT);
- ReadLockedPointer<Tool> const applicableTool = (seenT) ? reprap.GetTool(toolNumber) : reprap.GetCurrentOrDefaultTool();
+ MovementState& ms = GetMovementState(gb);
+ ReadLockedPointer<Tool> const applicableTool = (seenT) ? Tool::GetLockedTool(toolNumber) // if we were given a tool number, use that
+ : ms.GetLockedCurrentOrDefaultTool(); // else if we have a current tool, use that, else the default tool
// Check that we have a tool
if (applicableTool.IsNull())
@@ -1567,14 +1729,13 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
// Set the heater temperatures for that tool. We set the standby temperatures as well as the active ones,
- // because any slicer that uses M109 doesn't understand that there are separate active and standby temperatures.
+ // because any slicer that uses M104 or M109 doesn't understand that there are separate active and standby temperatures.
if (!IsSimulating())
{
- SetToolHeaters(applicableTool.Ptr(), temperature, true); // this may throw
+ SetToolHeaters(gb, applicableTool.Ptr(), temperature); // this may throw
}
- Tool * const currentTool = reprap.GetCurrentTool();
- if (code == 109 && currentTool == nullptr)
+ if (code == 109 && ms.currentTool == nullptr)
{
// Switch to the tool
if (!LockMovementAndWaitForStandstill(gb))
@@ -1582,23 +1743,23 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
return false;
}
- newToolNumber = applicableTool->Number();
- toolChangeParam = (IsSimulating()) ? 0 : DefaultToolChangeParam;
+ ms.newToolNumber = applicableTool->Number();
+ ms.toolChangeParam = (IsSimulating()) ? 0 : DefaultToolChangeParam;
gb.SetState(GCodeState::m109ToolChange0);
result = GCodeResult::ok;
}
else
{
- if (applicableTool.Ptr() == currentTool)
+ if (applicableTool.Ptr() == ms.currentTool)
{
// Even though the tool is selected, we may have turned it off e.g. when upgrading the WiFi firmware or following a heater fault that has been cleared.
// So make sure the tool heaters are on.
- reprap.SelectTool(applicableTool->Number(), IsSimulating());
+ ms.SelectTool(applicableTool->Number(), IsSimulating());
}
- else
+ else if (!IsSimulating())
{
// If we already have an active tool and we are setting temperatures for a different tool, set that tool's heaters to standby in case it is off
- reprap.StandbyTool(applicableTool->Number(), IsSimulating());
+ applicableTool->Standby();
}
if (code == 109 && !IsSimulating())
@@ -1671,7 +1832,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 114:
- GetCurrentCoordinates(reply);
+ HandleM114(gb, reply);
break;
case 115: // Print firmware version or set hardware type
@@ -1703,7 +1864,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
#endif
reply.printf("FIRMWARE_NAME: %s FIRMWARE_VERSION: %s ELECTRONICS: %s", FIRMWARE_NAME, VERSION, platform.GetElectronicsString());
-#ifdef DUET_NG
+#if defined(DUET_NG)
const char* const expansionName = DuetExpansion::GetExpansionBoardName();
if (expansionName != nullptr)
{
@@ -1714,8 +1875,16 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
reply.catf(" + %s", additionalExpansionName);
}
+#elif defined(DUET3MINI) && !defined(FMDC_V03)
+ {
+ const char *const expansionString = platform.GetExpansionBoardName();
+ if (expansionString != nullptr)
+ {
+ reply.catf(" + %s", expansionString);
+ }
+ }
#endif
-#ifdef DUET3_ATE
+#if defined(DUET3_ATE)
reply.lcatf("ATE firmware version %s date %s %s", Duet3Ate::GetFirmwareVersionString(), Duet3Ate::GetFirmwareDateString(), Duet3Ate::GetFirmwareTimeString());
#else
reply.catf(" FIRMWARE_DATE: %s%s", DATE, TIME_SUFFIX);
@@ -1724,19 +1893,19 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 116: // Wait for set temperatures
- if (!LockMovementAndWaitForStandstill(gb)) // wait until movement has finished and deferred command queue has caught up to avoid out-of-order execution
+ if (!LockMovementAndWaitForStandstillNoSync(gb)) // wait until movement has finished and deferred command queue has caught up to avoid out-of-order execution
{
return false;
}
if (!cancelWait)
{
- const float tolerance = (gb.Seen('S')) ? max<float>(gb.GetFValue(), 0.1) : TemperatureCloseEnough;
+ const float tolerance = (gb.Seen('S')) ? gb.GetPositiveFValue() : TemperatureCloseEnough;
bool seen = false;
if (gb.Seen('P'))
{
// Wait for the heaters associated with the specified tool to be ready
- if (!ToolHeatersAtSetTemperatures(reprap.GetTool(gb.GetIValue()).Ptr(), true, tolerance))
+ if (!ToolHeatersAtSetTemperatures(Tool::GetLockedTool(gb.GetIValue()).Ptr(), true, tolerance))
{
isWaiting = true;
return false;
@@ -2103,36 +2272,43 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 200: // Set filament diameter for volumetric extrusion and enable/disable volumetric extrusion
- if (gb.Seen('D'))
- {
- float diameters[MaxExtruders];
- size_t len = MaxExtruders;
- gb.GetFloatArray(diameters, len, true);
- for (size_t i = 0; i < len; ++i)
- {
- const float d = diameters[i];
- volumetricExtrusionFactors[i] = (d <= 0.0) ? 1.0 : 4.0/(fsquare(d) * Pi);
- }
- gb.LatestMachineState().volumetricExtrusion = (diameters[0] > 0.0);
- reprap.InputsUpdated();
- }
- else if (!gb.LatestMachineState().volumetricExtrusion)
- {
- reply.copy("Volumetric extrusion is disabled for this input source");
- }
- else
{
- reply.copy("Filament diameters for volumetric extrusion:");
- for (size_t i = 0; i < numExtruders; ++i)
+ bool seen = gb.Seen('S');
+ bool enable = !seen || gb.GetIValue() > 0;
+ if (gb.Seen('D'))
{
- const float vef = volumetricExtrusionFactors[i];
- if (vef == 1.0)
+ seen = true;
+ float diameters[MaxExtruders];
+ size_t len = MaxExtruders;
+ gb.GetFloatArray(diameters, len, true);
+ for (size_t i = 0; i < len; ++i)
{
- reply.cat(" n/a");
+ const float d = diameters[i];
+ if (d <= 0.0)
+ {
+ volumetricExtrusionFactors[i] = 1.0;
+ }
+ else
+ {
+ filamentDiameters[i] = d;
+ volumetricExtrusionFactors[i] = (enable) ? 4.0/(fsquare(d) * Pi) : 1.0;
+ }
}
- else
+ reprap.MoveUpdated();
+ }
+
+ if (seen)
+ {
+ gb.LatestMachineState().volumetricExtrusion = enable;
+ reprap.InputsUpdated();
+ }
+ else
+ {
+ reply.printf("Volumetric extrusion is %sabled for this input, filament diameters:",
+ (gb.LatestMachineState().volumetricExtrusion) ? "en" : "dis");
+ for (size_t i = 0; i < numExtruders; ++i)
{
- reply.catf(" %.03f", (double)(2.0/fastSqrtf(vef * Pi)));
+ reply.catf(" %.03f", (double)filamentDiameters[i]);
}
}
}
@@ -2249,7 +2425,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 204: // Set max travel and printing accelerations
- result = reprap.GetMove().ConfigureAccelerations(gb, reply);
+ result = ConfigureAccelerations(gb, reply);
break;
// For case 205 see case 566
@@ -2262,7 +2438,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen('P'))
{
const unsigned int toolNumber = gb.GetUIValue();
- auto tool = reprap.GetTool(toolNumber);
+ auto tool = Tool::GetLockedTool(toolNumber);
if (tool.IsNull())
{
reply.printf("Tool %u does not exist", toolNumber);
@@ -2275,7 +2451,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
else
{
- result = reprap.SetAllToolsFirmwareRetraction(gb, reply, outBuf);
+ result = Tool::SetAllToolsFirmwareRetraction(gb, reply, outBuf);
}
break;
@@ -2340,29 +2516,32 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 220: // Set/report speed factor override percentage
- if (gb.Seen('S'))
{
- const float newSpeedFactor = gb.GetFValue() * 0.01;
- if (newSpeedFactor >= 0.01)
+ MovementState& ms = GetMovementState(gb);
+ if (gb.Seen('S'))
{
- // If the last move hasn't gone yet, update its feed rate if it is not a firmware retraction
- if (moveState.segmentsLeft != 0 && moveState.applyM220M221)
+ const float newSpeedFactor = gb.GetPositiveFValue() * 0.01;
+ if (newSpeedFactor >= 0.01)
{
- moveState.feedRate *= newSpeedFactor / speedFactor;
+ // If the last move hasn't gone yet, update its feed rate if it is not a firmware retraction
+ if (ms.segmentsLeft != 0 && ms.applyM220M221)
+ {
+ ms.feedRate *= newSpeedFactor / ms.speedFactor;
+ }
+ ms.speedFactor = newSpeedFactor;
+ reprap.MoveUpdated();
+ }
+ else
+ {
+ reply.copy("Invalid speed factor");
+ result = GCodeResult::error;
}
- speedFactor = newSpeedFactor;
- reprap.MoveUpdated();
}
else
{
- reply.copy("Invalid speed factor");
- result = GCodeResult::error;
+ reply.printf("Speed factor: %.1f%%", (double)(ms.speedFactor * 100.0));
}
}
- else
- {
- reply.printf("Speed factor: %.1f%%", (double)(speedFactor * 100.0));
- }
break;
case 221: // Set/report extrusion factor override percentage
@@ -2374,7 +2553,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
extruder = gb.GetLimitedUIValue('D', numExtruders);
}
- const Tool * const ct = reprap.GetCurrentTool();
+ const Tool * const ct = GetMovementState(gb).currentTool;
if (!seenD && ct == nullptr)
{
reply.copy("No tool selected");
@@ -2382,7 +2561,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
else if (gb.Seen('S')) // S parameter sets the override percentage
{
- const float extrusionFactor = gb.GetFValue() * 0.01;
+ const float extrusionFactor = gb.GetPositiveFValue() * 0.01;
if (extrusionFactor >= 0.01)
{
if (seenD)
@@ -2486,6 +2665,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// Perform babystepping synchronously with moves. Only move axes that have been flagged as homed.
bool haveResidual = false;
+ MovementState& ms = GetMovementState(gb);
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
currentBabyStepOffsets[axis] += differences[axis];
@@ -2493,11 +2673,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (IsAxisHomed(axis))
{
const float amountPushed = reprap.GetMove().PushBabyStepping(axis, differences[axis]);
- moveState.initialCoords[axis] += amountPushed;
+ ms.initialCoords[axis] += amountPushed;
// The following causes all the remaining baby stepping that we didn't manage to push to be added to the [remainder of the] currently-executing move, if there is one.
// This could result in an abrupt Z movement, however the move will be processed as normal so the jerk limit will be honoured.
- moveState.coords[axis] += differences[axis];
+ ms.coords[axis] += differences[axis];
if (amountPushed != differences[axis])
{
haveResidual = true;
@@ -2505,15 +2685,14 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
}
- if (canMove && haveResidual && moveState.segmentsLeft == 0 && reprap.GetMove().NoLiveMovement())
+ if (canMove && haveResidual && ms.segmentsLeft == 0 && reprap.GetMove().NoLiveMovement())
{
// The pipeline is empty, so execute the babystepping move immediately if it is safe to do
- SetMoveBufferDefaults();
- moveState.feedRate = ConvertSpeedFromMmPerMin(DefaultFeedRate);
- moveState.tool = reprap.GetCurrentTool();
- moveState.linearAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetLinearAxes());
- moveState.rotationalAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetRotationalAxes());
- NewSingleSegmentMoveAvailable();
+ SetMoveBufferDefaults(ms);
+ ms.feedRate = ConvertSpeedFromMmPerMin(DefaultFeedRate);
+ ms.linearAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetLinearAxes());
+ ms.rotationalAxesMentioned = axesMentioned.Intersects(reprap.GetPlatform().GetRotationalAxes());
+ NewSingleSegmentMoveAvailable(ms);
}
}
else
@@ -2528,81 +2707,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 291: // Display message, optionally wait for acknowledgement
- {
- gb.MustSee('P');
- String<MaxMessageLength> message;
- gb.GetQuotedString(message.GetRef());
-
- bool dummy = false;
- String<MaxMessageLength> title;
- (void)gb.TryGetQuotedString('R', title.GetRef(), dummy);
-
- uint32_t sParam = 1;
- (void)gb.TryGetLimitedUIValue('S', sParam, dummy, 4);
-
- float tParam;
- if (sParam <= 1)
- {
- tParam = DefaultMessageTimeout;
- gb.TryGetFValue('T', tParam, dummy);
- }
- else
- {
- tParam = 0.0;
- }
-
- if (sParam == 0 && tParam <= 0.0)
- {
- reply.copy("Attempt to create a message box that cannot be dismissed");
- result = GCodeResult::error;
- break;
- }
-
- AxesBitmap axisControls;
- for (size_t axis = 0; axis < numTotalAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]) && gb.GetIValue() > 0)
- {
- axisControls.SetBit(axis);
- }
- }
-
- // Don't lock the movement system, because if we do then only the channel that issues the M291 can move the axes
- if (sParam >= 2)
- {
-#if HAS_SBC_INTERFACE
- if (reprap.UsingSbcInterface())
- {
- gb.SetState(GCodeState::waitingForAcknowledgement);
- }
-#endif
- if (Push(gb, true)) // stack the machine state including the file position
- {
- UnlockMovement(gb); // allow movement so that e.g. an SD card print can call M291 and then DWC or PanelDue can be used to jog axes
- gb.WaitForAcknowledgement(); // flag that we are waiting for acknowledgement
- }
- }
-
- // Display the message box on all relevant devices. Acknowledging any one of them clears them all.
- const MessageType mt = GetMessageBoxDevice(gb); // get the display device
- platform.SendAlert(mt, message.c_str(), title.c_str(), (int)sParam, tParam, axisControls);
- }
+ result = DoMessageBox(gb, reply);
break;
case 292: // Acknowledge message
- {
- reprap.ClearAlert();
-
- const bool cancelled = (gb.Seen('P') && gb.GetIValue() == 1);
- for (GCodeBuffer* targetGb : gcodeSources)
- {
- if (targetGb != nullptr)
- {
- targetGb->MessageAcknowledged(cancelled);
- }
- }
- platform.MessageF(MessageType::LogInfo, "M292: cancelled: %s", (cancelled ? "true" : "false"));
- }
+ result = AcknowledgeMessage(gb, reply);
break;
case 300: // Beep
@@ -2622,7 +2731,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
bool seen = false;
if (gb.Seen('P'))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -2631,7 +2740,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
if (gb.Seen('S'))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -2640,7 +2749,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
if (gb.Seen('R'))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -2701,7 +2810,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
if (gb.Seen(axisLetters[axis]))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -2723,7 +2832,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen(extrudeLetter))
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -2838,7 +2947,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// TODO support per-extruder values
if (gb.Seen('N'))
{
- platform.SetFilamentWidth(gb.GetFValue());
+ platform.SetFilamentWidth(gb.GetPositiveFValue());
break;
}
// no break
@@ -2858,12 +2967,12 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
#endif
const int seq = gb.Seen('R') ? gb.GetIValue() : -1;
- if (&gb == auxGCode && (type == 0 || type == 2))
+ if (&gb == AuxGCode() && (type == 0 || type == 2))
{
lastAuxStatusReportType = type;
}
- outBuf = GenerateJsonStatusResponse(type, seq, (&gb == auxGCode) ? ResponseSource::AUX : ResponseSource::Generic);
+ outBuf = GenerateJsonStatusResponse(type, seq, (&gb == AuxGCode()) ? ResponseSource::AUX : ResponseSource::Generic);
if (outBuf == nullptr)
{
result = GCodeResult::notFinished; // we ran out of buffers, so try again later
@@ -2879,7 +2988,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
bool dummy;
gb.TryGetQuotedString('K', key.GetRef(), dummy, true);
gb.TryGetQuotedString('F', flags.GetRef(), dummy, true);
- if (&gb == auxGCode)
+ if (&gb == AuxGCode())
{
lastAuxStatusReportType = ObjectModelAuxStatusReportType;
}
@@ -2890,7 +2999,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// We don't delay and retry here, in case the user asked for too much of the object model in one go for the output buffers to contain it
reply.copy("{\"err\":-1}\n");
}
- if (&gb == auxGCode)
+ if (&gb == AuxGCode())
{
gb.ResetReportDueTimer();
}
@@ -2943,7 +3052,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
if (gb.Seen('R'))
{
- laserMaxPower = max<float>(1.0, gb.GetFValue());
+ laserMaxPower = max<float>(1.0, gb.GetNonNegativeFValue());
}
}
reprap.StateUpdated();
@@ -2998,7 +3107,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
#endif
case 486: // number object or cancel object
- result = buildObjects.HandleM486(gb, reply, outBuf);
+ result = HandleM486(gb, reply, outBuf);
break;
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
@@ -3020,9 +3129,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 502: // Revert to default "factory settings" ignoring values in config-override.g
- if (!gb.LatestMachineState().runningM502) // avoid recursion
+ if (!gb.LatestMachineState().runningM502) // avoid recursion
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -3087,7 +3196,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen('P'))
{
// Lock movement to try to prevent other threads opening system files while we change the system path
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -3255,16 +3364,13 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen('S'))
{
- const float value = gb.GetFValue();
- if (value >= 10.0) // avoid divide by zero and silly results
+ const float value = gb.GetPositiveFValue();
+ for (size_t axis = 0; axis <= Z_AXIS; axis++)
{
- for (size_t axis = 0; axis <= Z_AXIS; axis++)
+ if (gb.Seen(axisLetters[axis]))
{
- if (gb.Seen(axisLetters[axis]))
- {
- move.SetAxisCompensation(axis, gb.GetFValue() / value);
- seen = true;
- }
+ move.SetAxisCompensation(axis, gb.GetFValue() / value);
+ seen = true;
}
}
}
@@ -3336,14 +3442,14 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (gb.Seen('P'))
{
const unsigned int heater = gb.GetLimitedUIValue('P', MaxHeaters);
- result = reprap.ClearTemperatureFault(heater, reply);
+ result = Tool::ClearTemperatureFault(heater, reply);
}
else
{
// Clear all heater faults
for (unsigned int heater = 0; heater < MaxHeaters; ++heater)
{
- result = max<GCodeResult>(result, reprap.ClearTemperatureFault(heater, reply));
+ result = max<GCodeResult>(result, Tool::ClearTemperatureFault(heater, reply));
}
}
break;
@@ -3513,12 +3619,12 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
platform.SetCommsProperties(chan, val);
if (chan == 0)
{
- usbGCode->SetCommsProperties(val);
+ UsbGCode()->SetCommsProperties(val);
}
#if HAS_AUX_DEVICES
else if (chan < NumSerialChannels)
{
- GCodeBuffer *& gbp = (chan == 1) ? auxGCode : aux2GCode;
+ GCodeBuffer * gbp = (chan == 1) ? AuxGCode() : Aux2GCode();
if (gbp != nullptr)
{
gbp->SetCommsProperties(val);
@@ -3589,7 +3695,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
#if SUPPORT_INKJET
case 578: // Fire Inkjet bits
- if (!LockMovementAndWaitForStandstill())
+ if (!LockMovementAndWaitForStandstillNoSync())
{
return false;
}
@@ -3749,6 +3855,15 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
result = reprap.GetMove().ConfigureMovementQueue(gb, reply);
break;
+#if SUPPORT_ASYNC_MOVES
+ case 596: // Select movement queue
+ result = SelectMovementQueue(gb, reply);
+ break;
+
+ case 597: // Collision avoidance
+ result = CollisionAvoidance(gb, reply);
+ break;
+#endif
// For cases 600 and 601, see 226
// M650 (set peel move parameters) and M651 (execute peel move) are no longer handled specially. Use macros to specify what they should do.
@@ -3773,16 +3888,23 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
const bool changed = move.GetKinematics().Configure(code, gb, reply, error);
if (changedMode)
{
- move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveState.coords);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
+ for (MovementState& ms : moveStates)
+ {
+ move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, ms.coords);
+ ToolOffsetInverseTransform(ms);
+ }
}
if (changed || changedMode)
{
- if (move.GetKinematics().LimitPosition(moveState.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, false, false) != LimitPositionResult::ok)
+ for (size_t i = 0; i < NumMovementSystems; ++i)
{
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // make sure the limits are reflected in the user position
+ MovementState& ms = moveStates[i];
+ if (move.GetKinematics().LimitPosition(ms.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, false, false) != LimitPositionResult::ok)
+ {
+ ToolOffsetInverseTransform(ms); // make sure the limits are reflected in the user position
+ }
+ move.SetNewPosition(ms.coords, true, i);
}
- move.SetNewPosition(moveState.coords, true);
SetAllAxesNotHomed();
reprap.MoveUpdated();
}
@@ -3808,50 +3930,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
#endif
case 667: // Set CoreXY mode
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- if (gb.Seen('S'))
- {
- const unsigned int mode = gb.GetLimitedUIValue('S', 3);
- Move& move = reprap.GetMove();
- const KinematicsType oldK = move.GetKinematics().GetKinematicsType(); // get the current kinematics type so we can tell whether it changed
-
- // Switch to the correct CoreXY mode
- switch (mode)
- {
- case 0:
- default: // to keep Eclipse happy
- move.SetKinematics(KinematicsType::cartesian);
- break;
-
- case 1:
- move.SetKinematics(KinematicsType::coreXY);
- break;
-
- case 2:
- move.SetKinematics(KinematicsType::coreXZ);
- break;
- }
-
- // We changed something, so reset the positions and set all axes not homed
- if (move.GetKinematics().GetKinematicsType() != oldK)
- {
- move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveState.coords);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
- }
- if (move.GetKinematics().LimitPosition(moveState.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, false, false) != LimitPositionResult::ok)
- {
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // make sure the limits are reflected in the user position
- }
- move.SetNewPosition(moveState.coords, true);
- SetAllAxesNotHomed();
- reprap.MoveUpdated();
- }
+ reply.copy("M667 is no longer supported - use M669 instead");
+ result = GCodeResult::error;
break;
- case 669: // Set kinematics and parameters for SCARA and other kinematics that don't use M665, M666 or M667
+ case 669: // Set kinematics and parameters for non-delta kinematics
if (!LockMovementAndWaitForStandstill(gb))
{
return false;
@@ -3882,16 +3965,20 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (seen)
{
// We changed something significant, so reset the positions and set all axes not homed
- if (move.GetKinematics().GetKinematicsType() != oldK)
- {
- move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveState.coords);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
- }
- if (move.GetKinematics().LimitPosition(moveState.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, false, false) != LimitPositionResult::ok)
+ for (size_t i = 0; i < NumMovementSystems; ++i)
{
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // make sure the limits are reflected in the user position
+ MovementState& ms = moveStates[i];
+ if (move.GetKinematics().GetKinematicsType() != oldK)
+ {
+ move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, ms.coords);
+ ToolOffsetInverseTransform(ms);
+ }
+ if (move.GetKinematics().LimitPosition(ms.coords, nullptr, numVisibleAxes, axesVirtuallyHomed, false, false) != LimitPositionResult::ok)
+ {
+ ToolOffsetInverseTransform(ms); // make sure the limits are reflected in the user position
+ }
+ move.SetNewPosition(ms.coords, true, i);
}
- move.SetNewPosition(moveState.coords, true);
SetAllAxesNotHomed();
reprap.MoveUpdated();
}
@@ -3927,7 +4014,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
reply.copy("Insufficient axes configured");
result = GCodeResult::error;
}
- else if (!LockMovementAndWaitForStandstill(gb))
+ else if (!LockMovementAndWaitForStandstillNoSync(gb))
{
result = GCodeResult::notFinished;
}
@@ -3965,7 +4052,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
const float a2 = (x1 == x2) ? y2 : x2;
// See what kind of compensation we need to perform
- SetMoveBufferDefaults();
+ MovementState& ms = GetMovementState(gb);
+ SetMoveBufferDefaults(ms);
if (axisToUse != 0)
{
if (!reprap.GetPlatform().IsAxisRotational(axisToUse))
@@ -3977,9 +4065,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// An axis letter is given, so try to level the given axis
const float correctionAngle = atanf((z2 - z1) / (a2 - a1)) * 180.0 / M_PI;
- const float correctionFactor = gb.Seen('S') ? gb.GetFValue() : 1.0;
- moveState.coords[axisToUse] += correctionAngle * correctionFactor;
- moveState.rotationalAxesMentioned = true;
+ const float correctionFactor = gb.Seen('S') ? gb.GetPositiveFValue() : 1.0;
+ ms.coords[axisToUse] += correctionAngle * correctionFactor;
+ ms.rotationalAxesMentioned = true;
+
reply.printf("%c axis is off by %.2f deg", axisLetters[axisToUse], (double)correctionAngle);
HandleReply(gb, GCodeResult::notFinished, reply.c_str());
}
@@ -3996,9 +4085,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
((z4 - z3) * (a2 - a1) - (z2 - z1) * (a4 - a3));
const float zS = ((z1 - z2) * (a4 * z3 - a3 * z4) - (z3 - z4) * (a2 * z1 - a1 * z2)) /
((z4 - z3) * (a2 - a1) - (z2 - z1) * (a4 - a3));
- moveState.coords[(x1 == x2) ? Y_AXIS : X_AXIS] += aS;
- moveState.coords[Z_AXIS] += zS;
- moveState.linearAxesMentioned = true;
+ ms.coords[(x1 == x2) ? Y_AXIS : X_AXIS] += aS;
+ ms.coords[Z_AXIS] += zS;
+ ms.linearAxesMentioned = true;
reply.printf("%c is offset by %.2fmm, Z is offset by %.2fmm", (x2 == x1) ? 'Y' : 'X', (double)aS, (double)zS);
HandleReply(gb, GCodeResult::notFinished, reply.c_str());
@@ -4015,10 +4104,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
gb.LatestMachineState().feedRate = gb.GetSpeed(); // don't apply the speed factor
}
- moveState.feedRate = gb.LatestMachineState().feedRate;
- moveState.usingStandardFeedrate = true;
- moveState.tool = reprap.GetCurrentTool();
- NewSingleSegmentMoveAvailable();
+ ms.feedRate = gb.LatestMachineState().feedRate;
+ ms.usingStandardFeedrate = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.SetState(GCodeState::waitingForSpecialMoveToComplete);
}
@@ -4076,20 +4164,23 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 703: // Configure Filament
- if (reprap.GetCurrentTool() != nullptr)
{
- const Filament *filament = reprap.GetCurrentTool()->GetFilament();
- if (filament != nullptr && filament->IsLoaded())
+ const Tool * const currentTool = GetMovementState(gb).currentTool;
+ if (currentTool != nullptr)
{
- String<StringLength256> scratchString;
- scratchString.printf("%s%s/%s", FILAMENTS_DIRECTORY, filament->GetName(), CONFIG_FILAMENT_G);
- DoFileMacro(gb, scratchString.c_str(), false, SystemHelperMacroCode);
+ const Filament *filament = currentTool->GetFilament();
+ if (filament != nullptr && filament->IsLoaded())
+ {
+ String<StringLength256> scratchString;
+ scratchString.printf("%s%s/%s", FILAMENTS_DIRECTORY, filament->GetName(), CONFIG_FILAMENT_G);
+ DoFileMacro(gb, scratchString.c_str(), false, SystemHelperMacroCode);
+ }
+ }
+ else
+ {
+ result = GCodeResult::error;
+ reply.copy("No tool selected");
}
- }
- else
- {
- result = GCodeResult::error;
- reply.copy("No tool selected");
}
break;
@@ -4099,7 +4190,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 751: // Register 3D scanner extension over USB
- if (&gb == usbGCode)
+ if (&gb == UsbGCode())
{
if (reprap.GetScanner().IsEnabled())
{
@@ -4278,13 +4369,13 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
#endif
)
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
}
seen = true;
- result = max(result, platform.SetMotorCurrent(axis, gb.GetFValue(), code, reply));
+ result = max(result, platform.SetMotorCurrent(axis, gb.GetPositiveFValue(), code, reply));
}
}
@@ -4296,7 +4387,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
#endif
)
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstillNoSync(gb))
{
return false;
}
@@ -4314,7 +4405,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (code == 906 && gb.Seen('I'))
{
seen = true;
- platform.SetIdleCurrentFactor(gb.GetFValue()/100.0);
+ platform.SetIdleCurrentFactor(gb.GetNonNegativeFValue()/100.0);
}
if (seen)
@@ -4350,7 +4441,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
case 911: // Enable auto save on loss of power
if (gb.Seen('S'))
{
- const float saveVoltage = gb.GetFValue();
+ const float saveVoltage = gb.GetPositiveFValue();
if (saveVoltage < 10.0)
{
platform.DisableAutoSave();
@@ -4465,7 +4556,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// For case 917, see 906
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
case 918: // Configure direct-connect display
# ifdef DUET_NG
// On Duet 2 configuring the display may affect the number of supported stepper drivers, so wait until there is no movement
@@ -4658,8 +4749,19 @@ bool GCodes::HandleTcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
return true; // when running M502 we don't execute T commands
}
+#if SUPPORT_ASYNC_MOVES
+ if (!gb.Executing())
+ {
+ UnlockAll(gb);
+ HandleReply(gb, GCodeResult::ok, "");
+ return true;
+ }
+#endif
+
bool seen = false;
int toolNum;
+ MovementState& ms = GetMovementState(gb);
+
if (gb.HasCommandNumber())
{
seen = true;
@@ -4673,50 +4775,65 @@ bool GCodes::HandleTcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
else if (gb.Seen('R'))
{
- const unsigned int rpNumber = gb.GetLimitedUIValue('R', ARRAY_SIZE(numberedRestorePoints));
+ const unsigned int rpNumber = gb.GetLimitedUIValue('R', NumVisibleRestorePoints);
seen = true;
- toolNum = numberedRestorePoints[rpNumber].toolNumber;
+ toolNum = ms.restorePoints[rpNumber].toolNumber;
}
if (seen)
{
- if (!LockMovementAndWaitForStandstill(gb))
+ if (!LockMovementAndWaitForStandstill(gb
+#if SUPPORT_ASYNC_MOVES
+ , false // the other motion system can run concurrently with the tool change
+#endif
+ ))
{
return false;
}
- if (buildObjects.IsCurrentObjectCancelled())
+#if SUPPORT_ASYNC_MOVES
+ if (toolNum >= 0)
+ {
+ // Check that the other motion system isn't already using the requested tool or changing to it
+ for (const MovementState& ms2 : moveStates)
+ {
+ if (&ms2 != &ms && (toolNum == ms2.GetCurrentToolNumber() || toolNum == ms2.newToolNumber))
+ {
+ UnlockAll(gb);
+ reply.printf("Tool %d is already in use by another motion system", toolNum);
+ HandleReply(gb, GCodeResult::error, reply.c_str());
+ return true;
+ }
+ }
+ }
+#endif
+ ms.newToolNumber = toolNum; // claim the new tool to prevent the other movement system doing so
+ if (ms.IsCurrentObjectCancelled())
{
- buildObjects.SetVirtualTool(toolNum); // don't do the tool change, just remember which one we are supposed to use
+ // Don't do the tool change, just remember which one we are supposed to use in 'newToolNumber'
+ }
+ else if (ms.GetCurrentToolNumber() != toolNum) // if old and new are the same we no longer follow the sequence. User can deselect and then reselect the tool if he wants the macros run.
+ {
+ StartToolChange(gb, (gb.Seen('P')) ? gb.GetUIValue() : DefaultToolChangeParam);
+ return true; // proceeding with state machine, so don't unlock or send a reply
}
else
{
- const Tool * const oldTool = reprap.GetCurrentTool();
- // If old and new are the same we no longer follow the sequence. User can deselect and then reselect the tool if he wants the macros run.
- if (oldTool == nullptr || oldTool->Number() != toolNum)
- {
- StartToolChange(gb, toolNum, (gb.Seen('P')) ? gb.GetUIValue() : DefaultToolChangeParam);
- return true; // proceeding with state machine, so don't unlock or send a reply
- }
- else
- {
- // Even though the tool is selected, we may have turned it off e.g. when upgrading the WiFi firmware or following a heater fault that has been cleared.
- // So make sure the tool heaters are on.
- reprap.SelectTool(toolNum, IsSimulating());
- }
+ // Even though the tool is selected, we may have turned it off e.g. when upgrading the WiFi firmware or following a heater fault that has been cleared. So make sure the tool heaters are on.
+ ms.SelectTool(toolNum, IsSimulating());
}
}
else
{
// Report the tool number in use if no parameter is passed
- const Tool * const tool = reprap.GetCurrentTool();
- if (tool == nullptr)
+ const int toolNum = ms.GetCurrentToolNumber();
+ if (toolNum < 0)
{
reply.copy("No tool is selected");
}
else
{
- reply.printf("Tool %d is selected", tool->Number());
+ reply.printf("Tool %d is selected", toolNum);
}
}
@@ -4729,6 +4846,13 @@ bool GCodes::HandleTcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
// This is called to handle internally-generated codes
bool GCodes::HandleQcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
+#if SUPPORT_ASYNC_MOVES
+ if (!gb.Executing())
+ {
+ return true;
+ }
+#endif
+
// Currently we don't need to worry about whether we are simulating or not
switch (gb.GetCommandNumber())
{
diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp
index f5d3acdb..b69a2101 100644
--- a/src/GCodes/GCodes3.cpp
+++ b/src/GCodes/GCodes3.cpp
@@ -54,8 +54,8 @@ GCodeResult GCodes::SavePosition(GCodeBuffer& gb, const StringRef& reply) THROWS
{
uint32_t sParam = 0;
bool dummySeen;
- gb.TryGetLimitedUIValue('S', sParam, dummySeen, NumRestorePoints);
- SavePosition(numberedRestorePoints[sParam], gb);
+ gb.TryGetLimitedUIValue('S', sParam, dummySeen, NumVisibleRestorePoints);
+ SavePosition(gb, sParam);
reprap.StateUpdated(); // tell DWC/DSF that a restore point has been changed
return GCodeResult::ok;
}
@@ -74,6 +74,7 @@ GCodeResult GCodes::SetPositions(GCodeBuffer& gb, const StringRef& reply) THROWS
// Don't wait for the machine to stop if only extruder drives are being reset.
// This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
AxesBitmap axesIncluded;
+ MovementState& ms = GetMovementState(gb);
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
if (gb.Seen(axisLetters[axis]))
@@ -87,25 +88,27 @@ GCodeResult GCodes::SetPositions(GCodeBuffer& gb, const StringRef& reply) THROWS
}
}
axesIncluded.SetBit(axis);
- moveState.currentUserPosition[axis] = gb.ConvertDistance(axisValue);
+ ms.currentUserPosition[axis] = gb.ConvertDistance(axisValue);
}
}
// Handle any E parameter in the G92 command
if (gb.Seen(extrudeLetter))
{
- virtualExtruderPosition = gb.GetDistance();
+ ms.latestVirtualExtruderPosition = gb.GetDistance();
}
if (axesIncluded.IsNonEmpty())
{
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
+ ToolOffsetTransform(ms);
- if (reprap.GetMove().GetKinematics().LimitPosition(moveState.coords, nullptr, numVisibleAxes, axesIncluded, false, limitAxes) != LimitPositionResult::ok)
+ if (reprap.GetMove().GetKinematics().LimitPosition(ms.coords, nullptr, numVisibleAxes, axesIncluded, false, limitAxes) != LimitPositionResult::ok)
{
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // make sure the limits are reflected in the user position
+ ToolOffsetInverseTransform(ms); // make sure the limits are reflected in the user position
}
- reprap.GetMove().SetNewPosition(moveState.coords, true);
+ reprap.GetMove().SetNewPosition(ms.coords, true, gb.GetActiveQueueNumber());
+ //TODO update the other move system too!
+
if (!IsSimulating())
{
axesHomed |= reprap.GetMove().GetKinematics().AxesAssumedHomed(axesIncluded);
@@ -116,19 +119,6 @@ GCodeResult GCodes::SetPositions(GCodeBuffer& gb, const StringRef& reply) THROWS
}
reprap.MoveUpdated(); // because we may have updated axesHomed or zDatumSetByProbing
}
-
-#if SUPPORT_ROLAND
- if (reprap.GetRoland()->Active())
- {
- for(size_t axis = 0; axis < AXES; axis++)
- {
- if (!reprap.GetRoland()->ProcessG92(moveState[axis], axis))
- {
- return GCodeResult::notFinished;
- }
- }
- }
-#endif
}
return GCodeResult::ok;
@@ -144,7 +134,17 @@ GCodeResult GCodes::OffsetAxes(GCodeBuffer& gb, const StringRef& reply)
{
if (gb.Seen(axisLetters[axis]))
{
- workplaceCoordinates[moveState.currentCoordinateSystem][axis] = -gb.GetDistance();
+ if (!LockMovementAndWaitForStandstill(gb))
+ {
+ return GCodeResult::notFinished;
+ }
+#if SUPPORT_ASYNC_MOVES
+ if (!gb.Executing())
+ {
+ return GCodeResult::ok;
+ }
+#endif
+ workplaceCoordinates[GetMovementState(gb).currentCoordinateSystem][axis] = -gb.GetDistance();
seen = true;
}
}
@@ -174,9 +174,10 @@ GCodeResult GCodes::GetSetWorkplaceCoordinates(GCodeBuffer& gb, const StringRef&
uint32_t cs = 0;
bool dummySeen;
gb.TryGetLimitedUIValue('P', cs, dummySeen, NumCoordinateSystems + 1); // allow 0..NumCoordinateSystems inclusive
+ MovementState& ms = GetMovementState(gb);
if (cs == 0)
{
- cs = moveState.currentCoordinateSystem + 1;
+ cs = ms.currentCoordinateSystem + 1;
}
bool seen = false;
@@ -191,9 +192,16 @@ GCodeResult GCodes::GetSetWorkplaceCoordinates(GCodeBuffer& gb, const StringRef&
{
return GCodeResult::notFinished;
}
+#if SUPPORT_ASYNC_MOVES
+ // Now that we have synced, we need only continue if we are primary
+ if (!gb.Executing())
+ {
+ return GCodeResult::ok;
+ }
+#endif
seen = true;
}
- workplaceCoordinates[cs - 1][axis] = (compute) ? moveState.currentUserPosition[axis] - coord : coord;
+ workplaceCoordinates[cs - 1][axis] = (compute) ? ms.currentUserPosition[axis] - coord : coord;
}
}
@@ -246,172 +254,6 @@ bool GCodes::WriteWorkplaceCoordinates(FileStore *f) const noexcept
#endif
-// Define the probing grid, called when we see an M557 command
-GCodeResult GCodes::DefineGrid(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException)
-{
- if (!LockMovement(gb)) // to ensure that probing is not already in progress
- {
- return GCodeResult::notFinished;
- }
-
- bool seenR = false, seenP = false, seenS = false;
- char axesLetters[2] = { 'X', 'Y'};
- float axis0Values[2];
- float axis1Values[2];
- float spacings[2] = { DefaultGridSpacing, DefaultGridSpacing };
-
- size_t axesSeenCount = 0;
- for (size_t axis = 0; axis < numVisibleAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- if (axisLetters[axis] == 'Z')
- {
- reply.copy("Z axis is not allowed for mesh leveling");
- return GCodeResult::error;
- }
- else if (axesSeenCount > 2)
- {
- reply.copy("Mesh leveling expects exactly two axes");
- return GCodeResult::error;
- }
- bool dummy;
- if (gb.TryGetFloatArray(
- axisLetters[axis],
- 2,
- (axesSeenCount == 0) ? axis0Values : axis1Values,
- reply,
- dummy,
- false))
- {
- return GCodeResult::error;
- }
- axesLetters[axesSeenCount] = axisLetters[axis];
- ++axesSeenCount;
- }
- }
- if (axesSeenCount == 1)
- {
- reply.copy("Specify zero or two axes in M557");
- return GCodeResult::error;
- }
- const bool axesSeen = axesSeenCount > 0;
-
- uint32_t numPoints[2];
- if (gb.TryGetUIArray('P', 2, numPoints, reply, seenP, true))
- {
- return GCodeResult::error;
- }
- if (!seenP)
- {
- if (gb.TryGetFloatArray('S', 2, spacings, reply, seenS, true))
- {
- return GCodeResult::error;
- }
- }
-
- float radius = -1.0;
- gb.TryGetFValue('R', radius, seenR);
-
- if (!axesSeen && !seenR && !seenS && !seenP)
- {
- // Just print the existing grid parameters
- if (defaultGrid.IsValid())
- {
- reply.copy("Grid: ");
- defaultGrid.PrintParameters(reply);
- }
- else
- {
- reply.copy("Grid is not defined");
- }
- return GCodeResult::ok;
- }
-
- if (!axesSeen && !seenR)
- {
- // Must have given just the S or P parameter
- reply.copy("specify at least radius or two axis ranges in M557");
- return GCodeResult::error;
- }
-
- if (axesSeen)
- {
- // Seen both axes
- if (seenP)
- {
- // In the following, we multiply the spacing by 0.9999 to ensure that when we divide the axis range by the spacing, we get the correct number of points
- // Otherwise, for some values we occasionally get one less point
- if (spacings[0] >= 2 && axis0Values[1] > axis0Values[0])
- {
- spacings[0] = (axis0Values[1] - axis0Values[0])/(numPoints[0] - 1) * 0.9999;
- }
- if (spacings[1] >= 2 && axis1Values[1] > axis1Values[0])
- {
- spacings[1] = (axis1Values[1] - axis1Values[0])/(numPoints[1] - 1) * 0.9999;
- }
- }
- }
- else
- {
- // Seen R
- if (radius > 0.0)
- {
- float effectiveXRadius;
- if (seenP && numPoints[0] >= 2)
- {
- effectiveXRadius = radius - 0.1;
- if (numPoints[1] % 2 == 0)
- {
- effectiveXRadius *= fastSqrtf(1.0 - 1.0/(float)((numPoints[1] - 1) * (numPoints[1] - 1)));
- }
- spacings[0] = (2 * effectiveXRadius)/(numPoints[0] - 1);
- }
- else
- {
- effectiveXRadius = floorf((radius - 0.1)/spacings[0]) * spacings[0];
- }
- axis0Values[0] = -effectiveXRadius;
- axis0Values[1] = effectiveXRadius + 0.1;
-
- float effectiveYRadius;
- if (seenP && numPoints[1] >= 2)
- {
- effectiveYRadius = radius - 0.1;
- if (numPoints[0] % 2 == 0)
- {
- effectiveYRadius *= fastSqrtf(1.0 - 1.0/(float)((numPoints[0] - 1) * (numPoints[0] - 1)));
- }
- spacings[1] = (2 * effectiveYRadius)/(numPoints[1] - 1);
- }
- else
- {
- effectiveYRadius = floorf((radius - 0.1)/spacings[1]) * spacings[1];
- }
- axis1Values[0] = -effectiveYRadius;
- axis1Values[1] = effectiveYRadius + 0.1;
- }
- else
- {
- reply.copy("M577 radius must be positive unless X and Y are specified");
- return GCodeResult::error;
- }
- }
-
- const bool ok = defaultGrid.Set(axesLetters, axis0Values, axis1Values, radius, spacings);
- reprap.MoveUpdated();
- if (ok)
- {
- return GCodeResult::ok;
- }
-
- const float axis1Range = axesSeen ? axis0Values[1] - axis0Values[0] : 2 * radius;
- const float axis2Range = axesSeen ? axis1Values[1] - axis1Values[0] : 2 * radius;
- reply.copy("bad grid definition: ");
- defaultGrid.PrintError(axis1Range, axis2Range, reply);
- return GCodeResult::error;
-}
-
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
@@ -435,8 +277,10 @@ GCodeResult GCodes::SimulateFile(GCodeBuffer& gb, const StringRef &reply, const
if (!IsSimulating())
{
axesVirtuallyHomed = AxesBitmap::MakeLowestNBits(numVisibleAxes); // pretend all axes are homed
- SavePosition(simulationRestorePoint, gb);
- simulationRestorePoint.feedRate = gb.LatestMachineState().feedRate;
+ for (MovementState& ms : moveStates)
+ {
+ ms.SavePosition(SimulationRestorePointNumber, numVisibleAxes, gb.LatestMachineState().feedRate, gb.GetJobFilePosition());
+ }
}
simulationTime = 0.0;
exitSimulationWhenFileComplete = true;
@@ -476,7 +320,10 @@ GCodeResult GCodes::ChangeSimulationMode(GCodeBuffer& gb, const StringRef &reply
{
// Starting a new simulation, so save the current position
axesVirtuallyHomed = AxesBitmap::MakeLowestNBits(numVisibleAxes); // pretend all axes are homed
- SavePosition(simulationRestorePoint, gb);
+ for (MovementState& ms : moveStates)
+ {
+ ms.SavePosition(SimulationRestorePointNumber, numVisibleAxes, gb.LatestMachineState().feedRate, gb.GetJobFilePosition());
+ }
}
simulationTime = 0.0;
}
@@ -490,7 +337,7 @@ GCodeResult GCodes::ChangeSimulationMode(GCodeBuffer& gb, const StringRef &reply
#endif
// Handle M577
-GCodeResult GCodes::WaitForPin(GCodeBuffer& gb, const StringRef &reply)
+GCodeResult GCodes::WaitForPin(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException)
{
AxesBitmap endstopsToWaitFor;
for (size_t axis = 0; axis < numTotalAxes; ++axis)
@@ -527,35 +374,22 @@ GCodeResult GCodes::WaitForPin(GCodeBuffer& gb, const StringRef &reply)
}
// Handle M581
-GCodeResult GCodes::ConfigureTrigger(GCodeBuffer& gb, const StringRef& reply)
+GCodeResult GCodes::ConfigureTrigger(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- gb.MustSee('T');
- const unsigned int triggerNumber = gb.GetUIValue();
- if (triggerNumber < MaxTriggers)
- {
- return triggers[triggerNumber].Configure(triggerNumber, gb, reply);
- }
-
- reply.copy("Trigger number out of range");
- return GCodeResult::error;
+ const unsigned int triggerNumber = gb.GetLimitedUIValue('T', MaxTriggers);
+ return triggers[triggerNumber].Configure(triggerNumber, gb, reply);
}
// Handle M582
-GCodeResult GCodes::CheckTrigger(GCodeBuffer& gb, const StringRef& reply)
+GCodeResult GCodes::CheckTrigger(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- gb.MustSee('T');
- const unsigned int triggerNumber = gb.GetUIValue();
- if (triggerNumber < MaxTriggers)
+ const unsigned int triggerNumber = gb.GetLimitedUIValue('T', MaxTriggers);
+ const bool unconditional = gb.Seen('S') && gb.GetUIValue() == 1;
+ if (unconditional || triggers[triggerNumber].CheckLevel())
{
- if (triggers[triggerNumber].CheckLevel())
- {
- triggersPending.SetBit(triggerNumber);
- }
- return GCodeResult::ok;
+ triggersPending.SetBit(triggerNumber);
}
-
- reply.copy("Trigger number out of range");
- return GCodeResult::error;
+ return GCodeResult::ok;
}
// Deal with a M584
@@ -656,8 +490,11 @@ GCodeResult GCodes::DoDriveMapping(GCodeBuffer& gb, const StringRef& reply) THRO
numVisibleAxes = numTotalAxes; // assume any new axes are visible unless there is a P parameter
float initialCoords[MaxAxes];
reprap.GetMove().GetKinematics().GetAssumedInitialPosition(drive + 1, initialCoords);
- moveState.coords[drive] = initialCoords[drive]; // user has defined a new axis, so set its position
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
+ for (MovementState& ms : moveStates)
+ {
+ ms.coords[drive] = initialCoords[drive]; // user has defined a new axis, so set its position
+ ToolOffsetInverseTransform(ms);
+ }
reprap.MoveUpdated();
}
platform.SetAxisDriversConfig(drive, numValues, drivers);
@@ -711,8 +548,13 @@ GCodeResult GCodes::DoDriveMapping(GCodeBuffer& gb, const StringRef& reply) THRO
{
// In the DDA ring, the axis positions for invisible non-moving axes are not always copied over from previous moves.
// So if we have more visible axes than before, then we need to update their positions to get them in sync.
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords); // ensure that the position of any new axes are updated in moveBuffer
- reprap.GetMove().SetNewPosition(moveState.coords, true); // tell the Move system where the axes are
+ //TODO for multiple motion systems, is this correct? Other input channel must wait until we have finished.
+ for (size_t i = 0; i < NumMovementSystems; ++i)
+ {
+ MovementState& ms = moveStates[i];
+ ToolOffsetTransform(ms); // ensure that the position of any new axes are updated in moveBuffer
+ reprap.GetMove().SetNewPosition(ms.coords, true, i); // tell the Move system where the axes are
+ }
}
#if SUPPORT_CAN_EXPANSION
rslt = max(rslt, platform.UpdateRemoteStepsPerMmAndMicrostepping(axesToUpdate, reply));
@@ -793,7 +635,8 @@ void GCodes::SwitchToExpansionMode() noexcept
GCodeResult GCodes::StraightProbe(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
const int8_t fraction = gb.GetCommandFraction();
- if (fraction < 2 || fraction > 5) {
+ if (fraction < 2 || fraction > 5)
+ {
return GCodeResult::warningNotSupported;
}
/*
@@ -827,7 +670,8 @@ GCodeResult GCodes::StraightProbe(GCodeBuffer& gb, const StringRef& reply) THROW
// Get the target coordinates (as user position) and check if we would move at all
float userPositionTarget[MaxAxes];
- memcpyf(userPositionTarget, moveState.currentUserPosition, numVisibleAxes);
+ MovementState& ms = GetMovementState(gb);
+ memcpyf(userPositionTarget, ms.currentUserPosition, numVisibleAxes);
bool seen = false;
bool doesMove = false;
@@ -840,8 +684,8 @@ GCodeResult GCodes::StraightProbe(GCodeBuffer& gb, const StringRef& reply) THROW
// Get the user provided target coordinate
// - If prefixed by G53 add the ToolOffset that will be subtracted below in ToolOffsetTransform as we ignore any offsets when G53 is active
// - otherwise add current workplace offsets so we go where the user expects to go
- // comparable to hoe DoStraightMove/DoArcMove does it
- const float axisTarget = gb.GetDistance() + (gb.LatestMachineState().g53Active ? GetCurrentToolOffset(axis) : GetWorkplaceOffset(axis));
+ // comparable to how DoStraightMove/DoArcMove does it
+ const float axisTarget = gb.GetDistance() + (gb.LatestMachineState().g53Active ? ms.GetCurrentToolOffset(axis) : GetWorkplaceOffset(gb, axis));
if (axisTarget != userPositionTarget[axis])
{
doesMove = true;
@@ -875,7 +719,7 @@ GCodeResult GCodes::StraightProbe(GCodeBuffer& gb, const StringRef& reply) THROW
return GCodeResult::ok;
}
// Convert target user position to machine coordinates and save them in StraightProbeSettings
- ToolOffsetTransform(userPositionTarget, straightProbeSettings.GetTarget());
+ ToolOffsetTransform(ms, userPositionTarget, straightProbeSettings.GetTarget());
// See whether we are using a user-defined Z probe or just current one
const size_t probeToUse = (gb.Seen('K') || gb.Seen('P')) ? gb.GetUIValue() : 0;
@@ -913,7 +757,8 @@ size_t GCodes::FindAxisLetter(GCodeBuffer& gb) THROWS(GCodeException)
// Deal with a M585
GCodeResult GCodes::ProbeTool(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- if (reprap.GetCurrentTool() == nullptr)
+ MovementState& ms = GetMovementState(gb);
+ if (ms.currentTool == nullptr)
{
reply.copy("No tool selected!");
return GCodeResult::error;
@@ -947,8 +792,8 @@ GCodeResult GCodes::ProbeTool(GCodeBuffer& gb, const StringRef& reply) THROWS(GC
}
// Decide which way and how far to go
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
- m585Settings.probingLimit = (gb.Seen('R')) ? moveState.coords[m585Settings.axisNumber] + gb.GetDistance()
+ ToolOffsetTransform(ms);
+ m585Settings.probingLimit = (gb.Seen('R')) ? ms.coords[m585Settings.axisNumber] + gb.GetDistance()
: (gb.Seen('S') && gb.GetIValue() > 0) ? platform.AxisMinimum(m585Settings.axisNumber)
: platform.AxisMaximum(m585Settings.axisNumber);
if (m585Settings.useProbe)
@@ -990,23 +835,24 @@ bool GCodes::SetupM585ProbingMove(GCodeBuffer& gb) noexcept
return false;
}
- SetMoveBufferDefaults();
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
- moveState.feedRate = m585Settings.feedRate;
- moveState.coords[m585Settings.axisNumber] = m585Settings.probingLimit;
- moveState.reduceAcceleration = reduceAcceleration;
- moveState.checkEndstops = true;
- moveState.canPauseAfter = false;
+ MovementState& ms = GetMovementState(gb);
+ SetMoveBufferDefaults(ms);
+ ToolOffsetTransform(ms);
+ ms.feedRate = m585Settings.feedRate;
+ ms.coords[m585Settings.axisNumber] = m585Settings.probingLimit;
+ ms.reduceAcceleration = reduceAcceleration;
+ ms.checkEndstops = true;
+ ms.canPauseAfter = false;
zProbeTriggered = false;
- moveState.linearAxesMentioned = reprap.GetPlatform().IsAxisLinear(m585Settings.axisNumber);
- moveState.rotationalAxesMentioned = reprap.GetPlatform().IsAxisRotational(m585Settings.axisNumber);
- NewSingleSegmentMoveAvailable();
+ ms.linearAxesMentioned = reprap.GetPlatform().IsAxisLinear(m585Settings.axisNumber);
+ ms.rotationalAxesMentioned = reprap.GetPlatform().IsAxisRotational(m585Settings.axisNumber);
+ NewSingleSegmentMoveAvailable(ms);
return true;
}
GCodeResult GCodes::FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- if (reprap.GetCurrentTool() == nullptr)
+ if (GetMovementState(gb).currentTool == nullptr)
{
reply.copy("No tool selected!");
return GCodeResult::error;
@@ -1048,29 +894,31 @@ bool GCodes::SetupM675ProbingMove(GCodeBuffer& gb, bool towardsMin) noexcept
return false;
}
- SetMoveBufferDefaults();
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
- moveState.coords[m675Settings.axisNumber] = towardsMin ? platform.AxisMinimum(m675Settings.axisNumber) : platform.AxisMaximum(m675Settings.axisNumber);
- moveState.feedRate = m675Settings.feedRate;
- moveState.checkEndstops = true;
- moveState.canPauseAfter = false;
+ MovementState& ms = GetMovementState(gb);
+ SetMoveBufferDefaults(ms);
+ ToolOffsetTransform(ms);
+ ms.coords[m675Settings.axisNumber] = towardsMin ? platform.AxisMinimum(m675Settings.axisNumber) : platform.AxisMaximum(m675Settings.axisNumber);
+ ms.feedRate = m675Settings.feedRate;
+ ms.checkEndstops = true;
+ ms.canPauseAfter = false;
zProbeTriggered = false;
- moveState.linearAxesMentioned = reprap.GetPlatform().IsAxisLinear(m675Settings.axisNumber);
- moveState.rotationalAxesMentioned = reprap.GetPlatform().IsAxisRotational(m675Settings.axisNumber);
- NewSingleSegmentMoveAvailable(); // kick off the move
+ ms.linearAxesMentioned = reprap.GetPlatform().IsAxisLinear(m675Settings.axisNumber);
+ ms.rotationalAxesMentioned = reprap.GetPlatform().IsAxisRotational(m675Settings.axisNumber);
+ NewSingleSegmentMoveAvailable(ms); // kick off the move
return true;
}
void GCodes::SetupM675BackoffMove(GCodeBuffer& gb, float position) noexcept
{
- SetMoveBufferDefaults();
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
- moveState.coords[m675Settings.axisNumber] = position;
- moveState.feedRate = m675Settings.feedRate;
- moveState.canPauseAfter = false;
- moveState.linearAxesMentioned = reprap.GetPlatform().IsAxisLinear(m675Settings.axisNumber);
- moveState.rotationalAxesMentioned = reprap.GetPlatform().IsAxisRotational(m675Settings.axisNumber);
- NewSingleSegmentMoveAvailable();
+ MovementState& ms = GetMovementState(gb);
+ SetMoveBufferDefaults(ms);
+ ToolOffsetTransform(ms);
+ ms.coords[m675Settings.axisNumber] = position;
+ ms.feedRate = m675Settings.feedRate;
+ ms.canPauseAfter = false;
+ ms.linearAxesMentioned = reprap.GetPlatform().IsAxisLinear(m675Settings.axisNumber);
+ ms.rotationalAxesMentioned = reprap.GetPlatform().IsAxisRotational(m675Settings.axisNumber);
+ NewSingleSegmentMoveAvailable(ms);
}
// Deal with a M905
@@ -1362,15 +1210,15 @@ GCodeResult GCodes::ConfigureDriver(GCodeBuffer& gb, const StringRef& reply) THR
DriverId driverIds[drivesCount];
gb.GetDriverIdArray(driverIds, drivesCount);
- bool const isEncoderReading = (gb.GetCommandFraction() == 3);
- if (isEncoderReading)
+ bool const isSetOfReadings = (gb.GetCommandFraction() == 3 || gb.GetCommandFraction() == 8 );
+ if (isSetOfReadings)
{
reply.copy("[");
}
- // Hangprinter needs M569 to support multiple P parameters in M569.3 and M569.4. This poses a problem for other uses of M569 because the output may be too long
+ // Hangprinter needs M569 to support multiple P parameters in M569.3, M569.4, and M569.8. This poses a problem for other uses of M569 because the output may be too long
// to fit in the reply buffer, and we can only use an OutputBuffer instead if the overall result is success.
- // Therefore we only support multiple P parameters for subfunctions 3 and 4.
+ // Therefore we only support multiple P parameters for subfunctions 3, 4, and 8.
GCodeResult res = GCodeResult::ok;
for (size_t i = 0; i < drivesCount; ++i)
{
@@ -1382,13 +1230,13 @@ GCodeResult GCodes::ConfigureDriver(GCodeBuffer& gb, const StringRef& reply) THR
:
#endif
ConfigureLocalDriver(gb, reply, id.localDriver);
- if (res != GCodeResult::ok || (!isEncoderReading && gb.GetCommandFraction() != 4))
+ if (res != GCodeResult::ok || (!isSetOfReadings && gb.GetCommandFraction() != 4))
{
break;
}
}
- if (isEncoderReading && res == GCodeResult::ok)
+ if (isSetOfReadings && res == GCodeResult::ok)
{
reply.cat(" ],\n");
}
@@ -1411,9 +1259,12 @@ GCodeResult GCodes::ConfigureLocalDriver(GCodeBuffer& gb, const StringRef& reply
case 1:
case 3:
+ case 4:
case 5:
case 6:
- // Main board drivers do not support closed loop modes, or reading encoders
+ case 8:
+ // Main board drivers do not support closed loop modes, or reading encoders,
+ // or reading motor currents through the subfunction 8
reply.copy("Command is not supported on local drivers");
return GCodeResult::error;
@@ -1666,34 +1517,41 @@ GCodeResult GCodes::HandleG68(GCodeBuffer& gb, const StringRef& reply) THROWS(GC
{
return GCodeResult::notFinished;
}
- if (gb.CurrentFileMachineState().selectedPlane != 0)
+
+#if SUPPORT_ASYNC_MOVES
+ if (gb.Executing())
+#endif
{
- reply.copy("this command may only be used when the selected plane is XY");
- return GCodeResult::error;
- }
+ if (gb.CurrentFileMachineState().selectedPlane != 0)
+ {
+ reply.copy("this command may only be used when the selected plane is XY");
+ return GCodeResult::error;
+ }
- float angle, centreX, centreY;
- gb.MustSee('R');
- angle = gb.GetFValue();
- gb.MustSee('A', 'X');
- centreX = gb.GetFValue();
- gb.MustSee('B', 'Y');
- centreY= gb.GetFValue();
+ float angle, centreX, centreY;
+ gb.MustSee('R');
+ angle = gb.GetFValue();
+ gb.MustSee('A', 'X');
+ centreX = gb.GetFValue();
+ gb.MustSee('B', 'Y');
+ centreY= gb.GetFValue();
- g68Centre[0] = centreX + GetWorkplaceOffset(0);
- g68Centre[1] = centreY + GetWorkplaceOffset(1);
- if (gb.Seen('I'))
- {
- g68Angle += angle;
- }
- else
- {
- g68Angle = angle;
+ g68Centre[0] = centreX + GetWorkplaceOffset(gb, 0);
+ g68Centre[1] = centreY + GetWorkplaceOffset(gb, 1);
+ if (gb.Seen('I'))
+ {
+ g68Angle += angle;
+ }
+ else
+ {
+ g68Angle = angle;
+ }
+ UpdateCurrentUserPosition(gb);
}
return GCodeResult::ok;
}
-// Account for coordinate rotation. Only called wheh the angle to rotate is nonzero, so we don't check that here.
+// Account for coordinate rotation. Only called when the angle to rotate is nonzero, so we don't check that here.
void GCodes::RotateCoordinates(float angleDegrees, float coords[2]) const noexcept
{
const float angle = angleDegrees * DegreesToRadians;
@@ -1708,11 +1566,12 @@ void GCodes::RotateCoordinates(float angleDegrees, float coords[2]) const noexce
// Change a live extrusion factor
void GCodes::ChangeExtrusionFactor(unsigned int extruder, float factor) noexcept
{
- if (moveState.segmentsLeft != 0 && moveState.applyM220M221)
+ const float multiplier = factor/extrusionFactors[extruder];
+ extrusionFactors[extruder] = factor;
+ for (MovementState& ms : moveStates)
{
- moveState.coords[ExtruderToLogicalDrive(extruder)] *= factor/extrusionFactors[extruder]; // last move not gone, so update it
+ ms.ChangeExtrusionFactor(extruder, multiplier);
}
- extrusionFactors[extruder] = factor;
reprap.MoveUpdated();
}
@@ -1790,42 +1649,34 @@ bool GCodes::ProcessWholeLineComment(GCodeBuffer& gb, const StringRef& reply) TH
switch (i)
{
case 1: // MESH (Cura)
-#if TRACK_OBJECT_NAMES
if (StringStartsWith(text, "NONMESH"))
{
- buildObjects.StopObject(gb);
+ StopObject(gb);
}
else
{
- buildObjects.StartObject(gb, text);
+ StartObject(gb, text);
}
-#endif
break;
case 9: // PRINTING (Ideamaker)
-#if TRACK_OBJECT_NAMES
if (StringStartsWith(text, "NON-OBJECT"))
{
- buildObjects.StopObject(gb);
+ StopObject(gb);
}
else
{
- buildObjects.StartObject(gb, text);
+ StartObject(gb, text);
}
-#endif
break;
case 0: // printing object (slic3r)
case 2: // process (S3D)
-#if TRACK_OBJECT_NAMES
- buildObjects.StartObject(gb, text);
-#endif
+ StartObject(gb, text);
break;
case 3: // stop printing object
-#if TRACK_OBJECT_NAMES
- buildObjects.StopObject(gb);
-#endif
+ StopObject(gb);
break;
case 4: // layer (counting from 1)
@@ -1956,7 +1807,7 @@ void GCodes::ProcessEvent(GCodeBuffer& gb) noexcept
platform.MessageF((MessageType)(mt & (LogLevelMask | ErrorMessageFlag | WarningMessageFlag)), "%s\n", eventText.c_str()); // log the event
}
const bool isPrinting = IsReallyPrinting();
- platform.SendAlert(GenericMessage, eventText.c_str(), (isPrinting) ? "Printing paused" : "Event notification", 1, 0.0, AxesBitmap());
+ reprap.SendSimpleAlert(GenericMessage, eventText.c_str(), (isPrinting) ? "Printing paused" : "Event notification");
if (IsReallyPrinting())
{
// We are going to pause. It may need to wait for the movement lock, so do it in a new state.
@@ -1971,10 +1822,10 @@ void GCodes::ProcessEvent(GCodeBuffer& gb) noexcept
#if !HAS_MASS_STORAGE && !HAS_EMBEDDED_FILES && defined(DUET_NG)
-// Function called by RepRap.cpp to enable PanelDue by default in the Duet 2 SBC build
+// Function called by RepRap.cpp to enable PanelDue by default in the Duet 2 SBC build so that we can test it in the ATE
void GCodes::SetAux0CommsProperties(uint32_t mode) const noexcept
{
- auxGCode->SetCommsProperties(mode);
+ AuxGCode()->SetCommsProperties(mode);
}
#endif
diff --git a/src/GCodes/GCodes4.cpp b/src/GCodes/GCodes4.cpp
index 3d7c6c08..7c89ca06 100644
--- a/src/GCodes/GCodes4.cpp
+++ b/src/GCodes/GCodes4.cpp
@@ -39,6 +39,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// Perform the next operation of the state machine for this gcode source
GCodeResult stateMachineResult = GCodeResult::ok;
+ MovementState& ms = GetMovementState(gb);
const GCodeState state = gb.GetState();
switch (state)
{
@@ -51,16 +52,16 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
)
{
// Check whether we made any G1 S3 moves and need to set the axis limits
- axesToSenseLength.Iterate([this](unsigned int axis, unsigned int)
+ axesToSenseLength.Iterate([this, &ms](unsigned int axis, unsigned int)
{
const EndStopPosition stopType = platform.GetEndstops().GetEndStopPosition(axis);
if (stopType == EndStopPosition::highEndStop)
{
- platform.SetAxisMaximum(axis, moveState.coords[axis], true);
+ platform.SetAxisMaximum(axis, ms.coords[axis], true);
}
else if (stopType == EndStopPosition::lowEndStop)
{
- platform.SetAxisMinimum(axis, moveState.coords[axis], true);
+ platform.SetAxisMinimum(axis, ms.coords[axis], true);
}
}
);
@@ -80,7 +81,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::waitingForSegmentedMoveToGo:
// Wait for all segments of the arc move to go into the movement queue and check whether an error occurred
- switch (moveState.segMoveState)
+ switch (ms.segMoveState)
{
case SegmentedMoveState::inactive: // move completed without error
gb.SetState(GCodeState::normal);
@@ -148,15 +149,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
}
}
- Tool * const currentTool = reprap.GetCurrentTool();
- if (currentTool != nullptr)
+ if (ms.currentTool != nullptr)
{
// We get here when the tool probe has been activated. In this case we know how far we
// went (i.e. the difference between our start and end positions) and if we need to
// incorporate any correction factors. That's why we only need to set the final tool
// offset to this value in order to finish the tool probing.
- const float coord = toolChangeRestorePoint.moveCoords[m585Settings.axisNumber] - moveState.currentUserPosition[m585Settings.axisNumber] + m585Settings.offset;
- currentTool->SetOffset(m585Settings.axisNumber, coord, true);
+ const float coord = ms.toolChangeRestorePoint.moveCoords[m585Settings.axisNumber] - ms.currentUserPosition[m585Settings.axisNumber] + m585Settings.offset;
+ ms.currentTool->SetOffset(m585Settings.axisNumber, coord, true);
}
gb.SetState(GCodeState::normal);
if (m585Settings.useProbe)
@@ -208,7 +208,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
zp->SetProbing(false);
if (zProbeTriggered)
{
- m675Settings.minDistance = moveState.currentUserPosition[m675Settings.axisNumber];
+ m675Settings.minDistance = ms.currentUserPosition[m675Settings.axisNumber];
SetupM675BackoffMove(gb, m675Settings.minDistance + m675Settings.backoffDistance);
gb.AdvanceState();
}
@@ -247,7 +247,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
zp->SetProbing(false);
if (zProbeTriggered)
{
- const float centre = (m675Settings.minDistance + moveState.currentUserPosition[m675Settings.axisNumber])/2;
+ const float centre = (m675Settings.minDistance + ms.currentUserPosition[m675Settings.axisNumber])/2;
SetupM675BackoffMove(gb, centre);
gb.AdvanceState();
}
@@ -327,24 +327,23 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::toolChange0: // run tfree for the old tool (if any)
case GCodeState::m109ToolChange0: // run tfree for the old tool (if any)
doingToolChange = true;
- SavePosition(toolChangeRestorePoint, gb);
- toolChangeRestorePoint.toolNumber = reprap.GetCurrentToolNumber();
- toolChangeRestorePoint.fanSpeed = lastDefaultFanSpeed;
- reprap.SetPreviousToolNumber();
+ SavePosition(gb, ToolChangeRestorePointNumber);
+ ms.toolChangeRestorePoint.toolNumber = ms.GetCurrentToolNumber();
+ ms.toolChangeRestorePoint.fanSpeed = ms.virtualFanSpeed;
+ ms.SetPreviousToolNumber();
reprap.StateUpdated(); // tell DWC/DSF that a restore point, nextToolNumber and the previousToolNumber have been updated
gb.AdvanceState();
// If the tool is in the firmware-retracted state, there may be some Z hop applied, which we must remove
- moveState.currentUserPosition[Z_AXIS] += moveState.currentZHop;
- moveState.currentZHop = 0.0;
+ ms.currentUserPosition[Z_AXIS] += ms.currentZHop;
+ ms.currentZHop = 0.0;
- if ((toolChangeParam & TFreeBit) != 0)
+ if ((ms.toolChangeParam & TFreeBit) != 0)
{
- const Tool * const oldTool = reprap.GetCurrentTool();
- if (oldTool != nullptr) // 2020-04-29: run tfree file even if not all axes have been homed
+ if (ms.currentTool != nullptr) // 2020-04-29: run tfree file even if not all axes have been homed
{
String<StringLength20> scratchString;
- scratchString.printf("tfree%d.g", oldTool->Number());
+ scratchString.printf("tfree%d.g", ms.currentTool->Number());
DoFileMacro(gb, scratchString.c_str(), false, ToolChangeMacroCode); // don't pass the T code here because it may be negative
}
}
@@ -352,19 +351,26 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::toolChange1: // release the old tool (if any), then run tpre for the new tool
case GCodeState::m109ToolChange1: // release the old tool (if any), then run tpre for the new tool
- if (LockMovementAndWaitForStandstill(gb)) // wait for tfree.g to finish executing
+ if (LockMovementAndWaitForStandstill(gb
+#if SUPPORT_ASYNC_MOVES
+ , false
+#endif
+ )) // wait for tfree.g to finish executing
{
- const Tool * const oldTool = reprap.GetCurrentTool();
- if (oldTool != nullptr)
+ if (ms.currentTool != nullptr)
{
- reprap.StandbyTool(oldTool->Number(), IsSimulating());
+ if (!IsSimulating())
+ {
+ ms.currentTool->Standby();
+ }
+ ms.currentTool = nullptr;
UpdateCurrentUserPosition(gb); // the tool offset may have changed, so get the current position
}
gb.AdvanceState();
- if (reprap.GetTool(newToolNumber).IsNotNull() && (toolChangeParam & TPreBit) != 0) // 2020-04-29: run tpre file even if not all axes have been homed
+ if (Tool::GetLockedTool(ms.newToolNumber).IsNotNull() && (ms.toolChangeParam & TPreBit) != 0) // 2020-04-29: run tpre file even if not all axes have been homed
{
String<StringLength20> scratchString;
- scratchString.printf("tpre%d.g", newToolNumber);
+ scratchString.printf("tpre%d.g", ms.newToolNumber);
DoFileMacro(gb, scratchString.c_str(), false, ToolChangeMacroCode);
}
}
@@ -372,16 +378,20 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::toolChange2: // select the new tool if it exists and run tpost
case GCodeState::m109ToolChange2: // select the new tool if it exists and run tpost
- if (LockMovementAndWaitForStandstill(gb)) // wait for tpre.g to finish executing
+ if (LockMovementAndWaitForStandstill(gb
+#if SUPPORT_ASYNC_MOVES
+ , false
+#endif
+ )) // wait for tpre.g to finish executing
{
- reprap.SelectTool(newToolNumber, IsSimulating());
+ ms.SelectTool(ms.newToolNumber, IsSimulating());
UpdateCurrentUserPosition(gb); // get the actual position of the new tool
gb.AdvanceState();
- if (reprap.GetCurrentTool() != nullptr && (toolChangeParam & TPostBit) != 0) // 2020-04-29: run tpost file even if not all axes have been homed
+ if (ms.currentTool != nullptr && (ms.toolChangeParam & TPostBit) != 0) // 2020-04-29: run tpost file even if not all axes have been homed
{
String<StringLength20> scratchString;
- scratchString.printf("tpost%d.g", newToolNumber);
+ scratchString.printf("tpost%d.g", ms.newToolNumber);
DoFileMacro(gb, scratchString.c_str(), false, ToolChangeMacroCode);
}
}
@@ -389,9 +399,13 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::toolChangeComplete:
case GCodeState::m109ToolChangeComplete:
- if (LockMovementAndWaitForStandstill(gb)) // wait for the move to height to finish
+ if (LockMovementAndWaitForStandstill(gb
+#if SUPPORT_ASYNC_MOVES
+ , false
+#endif
+ )) // wait for the move to height to finish
{
- gb.LatestMachineState().feedRate = toolChangeRestorePoint.feedRate;
+ gb.LatestMachineState().feedRate = ms.toolChangeRestorePoint.feedRate;
// We don't restore the default fan speed in case the user wants to use a different one for the new tool
doingToolChange = false;
@@ -408,7 +422,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
break;
case GCodeState::m109WaitForTemperature:
- if (cancelWait || IsSimulating() || ToolHeatersAtSetTemperatures(reprap.GetCurrentTool(), gb.LatestMachineState().waitWhileCooling, TemperatureCloseEnough))
+ if (cancelWait || IsSimulating() || ToolHeatersAtSetTemperatures(ms.currentTool, gb.LatestMachineState().waitWhileCooling, TemperatureCloseEnough))
{
cancelWait = isWaiting = false;
gb.SetState(GCodeState::normal);
@@ -452,7 +466,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
reply.printf((gb.GetState() == GCodeState::filamentChangePause2) ? "Printing paused for filament change at" : "Printing paused at");
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- reply.catf(" %c%.1f", axisLetters[axis], (double)pauseRestorePoint.moveCoords[axis]);
+ reply.catf(" %c%.1f", axisLetters[axis], (double)ms.pauseRestorePoint.moveCoords[axis]);
}
platform.MessageF(LogWarn, "%s\n", reply.c_str());
pauseState = PauseState::paused;
@@ -480,19 +494,18 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// Move the head back to the paused location
if (LockMovementAndWaitForStandstill(gb))
{
- const float currentZ = moveState.coords[Z_AXIS];
+ const float currentZ = ms.coords[Z_AXIS];
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- moveState.currentUserPosition[axis] = pauseRestorePoint.moveCoords[axis];
+ ms.currentUserPosition[axis] = ms.pauseRestorePoint.moveCoords[axis];
}
- SetMoveBufferDefaults();
- ToolOffsetTransform(moveState.currentUserPosition, moveState.coords);
- moveState.feedRate = ConvertSpeedFromMmPerMin(DefaultFeedRate); // ask for a good feed rate, we may have paused during a slow move
- moveState.tool = reprap.GetCurrentTool(); // needed so that bed compensation is applied correctly
- if (gb.GetState() == GCodeState::resuming1 && currentZ > pauseRestorePoint.moveCoords[Z_AXIS])
+ SetMoveBufferDefaults(ms);
+ ToolOffsetTransform(ms);
+ ms.feedRate = ConvertSpeedFromMmPerMin(DefaultFeedRate); // ask for a good feed rate, we may have paused during a slow move
+ if (gb.GetState() == GCodeState::resuming1 && currentZ > ms.pauseRestorePoint.moveCoords[Z_AXIS])
{
// First move the head to the correct XY point, then move it down in a separate move
- moveState.coords[Z_AXIS] = currentZ;
+ ms.coords[Z_AXIS] = currentZ;
gb.SetState(GCodeState::resuming2);
}
else
@@ -500,8 +513,8 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// Just move to the saved position in one go
gb.SetState(GCodeState::resuming3);
}
- moveState.linearAxesMentioned = moveState.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
- NewSingleSegmentMoveAvailable();
+ ms.linearAxesMentioned = ms.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
+ NewSingleSegmentMoveAvailable(ms);
}
break;
@@ -510,18 +523,17 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
{
// We no longer restore the paused fan speeds automatically on resuming, because that messes up the print cooling fan speed if a tool change has been done
// They can be restored manually in resume.g if required
- virtualExtruderPosition = pauseRestorePoint.virtualExtruderPosition; // reset the extruder position in case we are receiving absolute extruder moves
- moveState.virtualExtruderPosition = pauseRestorePoint.virtualExtruderPosition;
- fileGCode->LatestMachineState().feedRate = pauseRestorePoint.feedRate;
- moveFractionToSkip = pauseRestorePoint.proportionDone;
- restartInitialUserC0 = pauseRestorePoint.initialUserC0;
- restartInitialUserC1 = pauseRestorePoint.initialUserC1;
+ ms.moveStartVirtualExtruderPosition = ms.latestVirtualExtruderPosition = ms.pauseRestorePoint.virtualExtruderPosition; // reset the extruder position in case we are receiving absolute extruder moves
+ FileGCode()->LatestMachineState().feedRate = ms.pauseRestorePoint.feedRate;
+ ms.moveFractionToSkip = ms.pauseRestorePoint.proportionDone;
+ ms.restartInitialUserC0 = ms.pauseRestorePoint.initialUserC0;
+ ms.restartInitialUserC1 = ms.pauseRestorePoint.initialUserC1;
reply.copy("Printing resumed");
platform.Message(LogWarn, "Printing resumed\n");
pauseState = PauseState::notPaused;
- if (pausedInMacro)
+ if (ms.pausedInMacro)
{
- fileGCode->OriginalMachineState().firstCommandAfterRestart = true;
+ FileGCode()->OriginalMachineState().firstCommandAfterRestart = true;
}
gb.SetState(GCodeState::normal);
}
@@ -608,12 +620,17 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
gb.SetState(GCodeState::normal);
break;
- case GCodeState::stopping: // MO or M1 after executing stop.g/sleep.g if present
+ case GCodeState::stopping: // here when a print has finished, need to execute stop.g
if (LockMovementAndWaitForStandstill(gb))
{
- pauseState = PauseState::notPaused;
- platform.SetDriversIdle();
+#if SUPPORT_ASYNC_MOVES
+ gb.ExecuteAll(); // only fileGCode gets here so it needs to execute moves for all commands
+#endif
gb.SetState(GCodeState::normal);
+ if (!DoFileMacro(*FileGCode(), STOP_G, false, AsyncSystemMacroCode))
+ {
+ reprap.GetHeat().SwitchOffAll(true);
+ }
}
break;
@@ -622,38 +639,39 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
{
// Move to the current probe point
Move& move = reprap.GetMove();
- const GridDefinition& grid = move.AccessHeightMap().GetGrid();
- const float axis0Coord = grid.GetCoordinate(0, gridAxis0index);
- const float axis1Coord = grid.GetCoordinate(1, gridAxis1index);
- if (grid.IsInRadius(axis0Coord, axis1Coord))
+ const HeightMap& hm = move.AccessHeightMap();
+ if (hm.CanProbePoint(gridAxis0index, gridAxis1index))
{
+ const GridDefinition& grid = hm.GetGrid();
+ const float axis0Coord = grid.GetCoordinate(0, gridAxis0index);
+ const float axis1Coord = grid.GetCoordinate(1, gridAxis1index);
const size_t axis0Num = grid.GetAxisNumber(0);
const size_t axis1Num = grid.GetAxisNumber(1);
AxesBitmap axes;
axes.SetBit(axis0Num);
axes.SetBit(axis1Num);
float axesCoords[MaxAxes];
- memcpy(axesCoords, moveState.coords, sizeof(axesCoords)); // copy current coordinates of all other axes in case they are relevant to IsReachable
+ memcpy(axesCoords, ms.coords, sizeof(axesCoords)); // copy current coordinates of all other axes in case they are relevant to IsReachable
const auto zp = platform.GetZProbeOrDefault(currentZProbeNumber);
axesCoords[axis0Num] = axis0Coord - zp->GetOffset(axis0Num);
axesCoords[axis1Num] = axis1Coord - zp->GetOffset(axis1Num);
axesCoords[Z_AXIS] = zp->GetStartingHeight();
if (move.IsAccessibleProbePoint(axesCoords, axes))
{
- SetMoveBufferDefaults();
- moveState.coords[axis0Num] = axesCoords[axis0Num];
- moveState.coords[axis1Num] = axesCoords[axis1Num];
- moveState.coords[Z_AXIS] = zp->GetStartingHeight();
- moveState.feedRate = zp->GetTravelSpeed();
- moveState.linearAxesMentioned = moveState.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
- NewSingleSegmentMoveAvailable();
+ SetMoveBufferDefaults(ms);
+ ms.coords[axis0Num] = axesCoords[axis0Num];
+ ms.coords[axis1Num] = axesCoords[axis1Num];
+ ms.coords[Z_AXIS] = zp->GetStartingHeight();
+ ms.feedRate = zp->GetTravelSpeed();
+ ms.linearAxesMentioned = ms.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
+ NewSingleSegmentMoveAvailable(ms);
InitialiseTaps(false);
gb.AdvanceState();
}
else
{
- platform.MessageF(WarningMessage, "Skipping grid point %c=%.1f, %c=%.1f because Z probe cannot reach it\n", grid.GetAxisLetter(0), (double)axis0Coord, grid.GetAxisLetter(1), (double)axis1Coord);
+ platform.MessageF(WarningMessage, "Skipping grid point %c=%.1f, %c=%.1f because the Z probe cannot reach it\n", grid.GetAxisLetter(0), (double)axis0Coord, grid.GetAxisLetter(1), (double)axis1Coord);
gb.SetState(GCodeState::gridProbing6);
}
}
@@ -714,7 +732,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
else
{
zProbeTriggered = false;
- SetMoveBufferDefaults();
+ SetMoveBufferDefaults(ms);
if (!platform.GetEndstops().EnableZProbe(currentZProbeNumber) || !zp->SetProbing(true))
{
gb.LatestMachineState().SetError("Failed to enable probe");
@@ -722,12 +740,12 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
RetractZProbe(gb);
break;
}
- moveState.checkEndstops = true;
- moveState.reduceAcceleration = true;
- moveState.coords[Z_AXIS] = -zp->GetDiveHeight() + zp->GetActualTriggerHeight();
- moveState.feedRate = zp->GetProbingSpeed(tapsDone);
- moveState.linearAxesMentioned = true; // assume that both linear and rotational axes might be moving
- NewSingleSegmentMoveAvailable();
+ ms.checkEndstops = true;
+ ms.reduceAcceleration = true;
+ ms.coords[Z_AXIS] = -zp->GetDiveHeight() + zp->GetActualTriggerHeight();
+ ms.feedRate = zp->GetProbingSpeed(tapsDone);
+ ms.linearAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.AdvanceState();
}
}
@@ -744,7 +762,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
if (zp->GetProbeType() == ZProbeType::none)
{
// No Z probe, so we are doing manual mesh levelling. Take the current Z height as the height error.
- g30zHeightError = moveState.coords[Z_AXIS];
+ g30zHeightError = ms.coords[Z_AXIS];
}
else
{
@@ -758,7 +776,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
}
// Grid probing never does an additional fast tap, so we can always include this tap in the average
- g30zHeightError = moveState.coords[Z_AXIS] - zp->GetActualTriggerHeight();
+ g30zHeightError = ms.coords[Z_AXIS] - zp->GetActualTriggerHeight();
g30zHeightErrorSum += g30zHeightError;
}
@@ -772,14 +790,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::gridProbing4a: // ready to lift the probe after probing the current grid probe point
// Move back up to the dive height
- SetMoveBufferDefaults();
+ SetMoveBufferDefaults(ms);
{
const auto zp = platform.GetZProbeOrDefault(currentZProbeNumber);
- moveState.coords[Z_AXIS] = zp->GetStartingHeight();
- moveState.feedRate = zp->GetTravelSpeed();
+ ms.coords[Z_AXIS] = zp->GetStartingHeight();
+ ms.feedRate = zp->GetTravelSpeed();
}
- moveState.linearAxesMentioned = true;
- NewSingleSegmentMoveAvailable();
+ ms.linearAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.AdvanceState();
break;
@@ -913,14 +931,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// States used for G30 probing
case GCodeState::probingAtPoint0:
// Initial state when executing G30 with a P parameter. Start by moving to the dive height at the current position.
- SetMoveBufferDefaults();
+ SetMoveBufferDefaults(ms);
{
const auto zp = platform.GetZProbeOrDefault(currentZProbeNumber);
- moveState.coords[Z_AXIS] = zp->GetStartingHeight();
- moveState.feedRate = zp->GetTravelSpeed();
+ ms.coords[Z_AXIS] = zp->GetStartingHeight();
+ ms.feedRate = zp->GetTravelSpeed();
}
- moveState.linearAxesMentioned = true;
- NewSingleSegmentMoveAvailable();
+ ms.linearAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.AdvanceState();
break;
@@ -929,13 +947,13 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
if (LockMovementAndWaitForStandstill(gb))
{
// Head is at the dive height but needs to be moved to the correct XY position. The XY coordinates have already been stored.
- SetMoveBufferDefaults();
- (void)reprap.GetMove().GetProbeCoordinates(g30ProbePointIndex, moveState.coords[X_AXIS], moveState.coords[Y_AXIS], true);
+ SetMoveBufferDefaults(ms);
+ (void)reprap.GetMove().GetProbeCoordinates(g30ProbePointIndex, ms.coords[X_AXIS], ms.coords[Y_AXIS], true);
const auto zp = platform.GetZProbeOrDefault(currentZProbeNumber);
- moveState.coords[Z_AXIS] = zp->GetStartingHeight();
- moveState.feedRate = zp->GetTravelSpeed();
- moveState.linearAxesMentioned = moveState.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
- NewSingleSegmentMoveAvailable();
+ ms.coords[Z_AXIS] = zp->GetStartingHeight();
+ ms.feedRate = zp->GetTravelSpeed();
+ ms.linearAxesMentioned = ms.rotationalAxesMentioned = true; // assume that both linear and rotational axes might be moving
+ NewSingleSegmentMoveAvailable(ms);
InitialiseTaps(false);
gb.AdvanceState();
@@ -999,7 +1017,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
else
{
zProbeTriggered = false;
- SetMoveBufferDefaults();
+ SetMoveBufferDefaults(ms);
if (!platform.GetEndstops().EnableZProbe(currentZProbeNumber) || !zp->SetProbing(true))
{
gb.LatestMachineState().SetError("Failed to enable probe");
@@ -1008,14 +1026,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
break;
}
- moveState.checkEndstops = true;
- moveState.reduceAcceleration = true;
- moveState.coords[Z_AXIS] = (IsAxisHomed(Z_AXIS))
+ ms.checkEndstops = true;
+ ms.reduceAcceleration = true;
+ ms.coords[Z_AXIS] = (IsAxisHomed(Z_AXIS))
? platform.AxisMinimum(Z_AXIS) - zp->GetDiveHeight() + zp->GetActualTriggerHeight() // Z axis has been homed, so no point in going very far
: -1.1 * platform.AxisTotalLength(Z_AXIS); // Z axis not homed yet, so treat this as a homing move
- moveState.feedRate = zp->GetProbingSpeed(tapsDone);
- moveState.linearAxesMentioned = true;
- NewSingleSegmentMoveAvailable();
+ ms.feedRate = zp->GetProbingSpeed(tapsDone);
+ ms.linearAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.AdvanceState();
}
}
@@ -1035,7 +1053,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
if (zp->GetProbeType() == ZProbeType::none)
{
// No Z probe, so we are doing manual mesh levelling. Take the current Z height as the height error.
- g30zHeightError = moveState.coords[Z_AXIS];
+ g30zHeightError = ms.coords[Z_AXIS];
zp->SetLastStoppedHeight(g30zHeightError);
}
else
@@ -1076,16 +1094,16 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
if (tapsDone <= 1 && !hadProbingError)
{
// Reset the Z axis origin according to the height error so that we can move back up to the dive height
- moveState.coords[Z_AXIS] = zp->GetActualTriggerHeight();
- reprap.GetMove().SetNewPosition(moveState.coords, false);
+ ms.coords[Z_AXIS] = zp->GetActualTriggerHeight();
+ reprap.GetMove().SetNewPosition(ms.coords, false, gb.GetActiveQueueNumber());
// Find the coordinates of the Z probe to pass to SetZeroHeightError
float tempCoords[MaxAxes];
- memcpyf(tempCoords, moveState.coords, ARRAY_SIZE(tempCoords));
+ memcpyf(tempCoords, ms.coords, ARRAY_SIZE(tempCoords));
tempCoords[X_AXIS] += zp->GetOffset(X_AXIS);
tempCoords[Y_AXIS] += zp->GetOffset(Y_AXIS);
reprap.GetMove().SetZeroHeightError(tempCoords);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
+ ToolOffsetInverseTransform(ms);
g30zHeightErrorSum = g30zHeightError = 0; // there is no longer any height error from this probe
SetAxisIsHomed(Z_AXIS); // this is only correct if the Z axis is Cartesian-like, but other architectures must be homed before probing anyway
@@ -1103,14 +1121,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::probingAtPoint4a:
// Move back up to the dive height before we change anything, in particular before we adjust leadscrews
- SetMoveBufferDefaults();
+ SetMoveBufferDefaults(ms);
{
const auto zp = platform.GetZProbeOrDefault(currentZProbeNumber);
- moveState.coords[Z_AXIS] = zp->GetStartingHeight();
- moveState.feedRate = zp->GetTravelSpeed();
+ ms.coords[Z_AXIS] = zp->GetStartingHeight();
+ ms.feedRate = zp->GetTravelSpeed();
}
- moveState.linearAxesMentioned = true;
- NewSingleSegmentMoveAvailable();
+ ms.linearAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.AdvanceState();
break;
@@ -1161,16 +1179,16 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
else
{
// Setting the Z height with G30
- moveState.coords[Z_AXIS] -= g30zHeightError;
- reprap.GetMove().SetNewPosition(moveState.coords, false);
+ ms.coords[Z_AXIS] -= g30zHeightError;
+ reprap.GetMove().SetNewPosition(ms.coords, false, gb.GetActiveQueueNumber());
// Find the coordinates of the Z probe to pass to SetZeroHeightError
float tempCoords[MaxAxes];
- memcpyf(tempCoords, moveState.coords, ARRAY_SIZE(tempCoords));
+ memcpyf(tempCoords, ms.coords, ARRAY_SIZE(tempCoords));
tempCoords[X_AXIS] += zp->GetOffset(X_AXIS);
tempCoords[Y_AXIS] += zp->GetOffset(Y_AXIS);
reprap.GetMove().SetZeroHeightError(tempCoords);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
+ ToolOffsetInverseTransform(ms);
}
gb.AdvanceState();
if (zp->GetProbeType() != ZProbeType::blTouch) // if it's a BLTouch then we have already retracted it
@@ -1188,9 +1206,9 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
{
// G30 with a silly Z value and S=1 is equivalent to G30 with no parameters in that it sets the current Z height
// This is useful because it adjusts the XY position to account for the probe offset.
- moveState.coords[Z_AXIS] -= g30zHeightError;
- reprap.GetMove().SetNewPosition(moveState.coords, false);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition);
+ ms.coords[Z_AXIS] -= g30zHeightError;
+ reprap.GetMove().SetNewPosition(ms.coords, false, gb.GetActiveQueueNumber());
+ ToolOffsetInverseTransform(ms);
}
else if (g30SValue >= -1)
{
@@ -1219,15 +1237,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
else if (g30SValue == -2)
{
// Adjust the Z offset of the current tool to account for the height error
- Tool * const tool = reprap.GetCurrentTool();
- if (tool == nullptr)
+ if (ms.currentTool == nullptr)
{
gb.LatestMachineState().SetError("Tool was deselected during G30 S-2 command");
}
else
{
- tool->SetOffset(Z_AXIS, -g30zHeightError, true);
- ToolOffsetInverseTransform(moveState.coords, moveState.currentUserPosition); // update user coordinates to reflect the new tool offset
+ ms.currentTool->SetOffset(Z_AXIS, -g30zHeightError, true);
+ ToolOffsetInverseTransform(ms); // update user coordinates to reflect the new tool offset
}
}
else
@@ -1238,7 +1255,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
gb.SetState(GCodeState::normal);
break;
- case GCodeState::straightProbe0: // ready to deploy the probe
+ case GCodeState::straightProbe0: // ready to deploy the probe
if (LockMovementAndWaitForStandstill(gb))
{
gb.AdvanceState();
@@ -1292,7 +1309,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
else
{
zProbeTriggered = false;
- SetMoveBufferDefaults();
+ SetMoveBufferDefaults(ms);
if (!platform.GetEndstops().EnableZProbe(straightProbeSettings.GetZProbeToUse(), probingAway) || !zp->SetProbing(true))
{
gb.LatestMachineState().SetError("Failed to enable probe");
@@ -1301,12 +1318,12 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
break;
}
- moveState.checkEndstops = true;
- moveState.reduceAcceleration = true;
- straightProbeSettings.SetCoordsToTarget(moveState.coords);
- moveState.feedRate = zp->GetProbingSpeed(0);
- moveState.linearAxesMentioned = moveState.rotationalAxesMentioned = true;
- NewSingleSegmentMoveAvailable();
+ ms.checkEndstops = true;
+ ms.reduceAcceleration = true;
+ straightProbeSettings.SetCoordsToTarget(ms.coords);
+ ms.feedRate = zp->GetProbingSpeed(0);
+ ms.linearAxesMentioned = ms.rotationalAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
gb.AdvanceState();
}
}
@@ -1339,21 +1356,20 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// Firmware retraction/un-retraction states
case GCodeState::doingFirmwareRetraction:
// We just did the retraction part of a firmware retraction, now we need to do the Z hop
- if (moveState.segmentsLeft == 0)
- {
- const Tool * const tool = reprap.GetCurrentTool();
- if (tool != nullptr)
- {
- SetMoveBufferDefaults();
- moveState.tool = tool;
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, 0, moveState.tool);
- moveState.coords[Z_AXIS] += tool->GetRetractHop();
- moveState.feedRate = platform.MaxFeedrate(Z_AXIS);
- moveState.filePos = (&gb == fileGCode) ? gb.GetFilePosition() : noFilePosition;
- moveState.canPauseAfter = false; // don't pause after a retraction because that could cause too much retraction
- moveState.currentZHop = tool->GetRetractHop();
- moveState.linearAxesMentioned = true;
- NewSingleSegmentMoveAvailable();
+ if (ms.segmentsLeft == 0)
+ {
+ if (ms.currentTool != nullptr)
+ {
+ SetMoveBufferDefaults(ms);
+ ms.movementTool = ms.currentTool;
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, 0, ms.currentTool);
+ ms.coords[Z_AXIS] += ms.currentTool->GetRetractHop();
+ ms.feedRate = platform.MaxFeedrate(Z_AXIS);
+ ms.filePos = gb.GetJobFilePosition();
+ ms.canPauseAfter = false; // don't pause after a retraction because that could cause too much retraction
+ ms.currentZHop = ms.currentTool->GetRetractHop();
+ ms.linearAxesMentioned = true;
+ NewSingleSegmentMoveAvailable(ms);
}
gb.SetState(GCodeState::normal);
}
@@ -1361,22 +1377,21 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::doingFirmwareUnRetraction:
// We just undid the Z-hop part of a firmware un-retraction, now we need to do the un-retract
- if (moveState.segmentsLeft == 0)
+ if (ms.segmentsLeft == 0)
{
- const Tool * const tool = reprap.GetCurrentTool();
- if (tool != nullptr && tool->DriveCount() != 0)
+ if (ms.currentTool != nullptr && ms.currentTool->DriveCount() != 0)
{
- SetMoveBufferDefaults();
- moveState.tool = tool;
- reprap.GetMove().GetCurrentUserPosition(moveState.coords, 0, tool);
- for (size_t i = 0; i < tool->DriveCount(); ++i)
+ SetMoveBufferDefaults(ms);
+ ms.movementTool = ms.currentTool;
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, 0, ms.currentTool);
+ for (size_t i = 0; i < ms.currentTool->DriveCount(); ++i)
{
- moveState.coords[ExtruderToLogicalDrive(tool->GetDrive(i))] = tool->GetRetractLength() + tool->GetRetractExtra();
+ ms.coords[ExtruderToLogicalDrive(ms.currentTool->GetDrive(i))] = ms.currentTool->GetRetractLength() + ms.currentTool->GetRetractExtra();
}
- moveState.feedRate = tool->GetUnRetractSpeed() * tool->DriveCount();
- moveState.filePos = (&gb == fileGCode) ? gb.GetFilePosition() : noFilePosition;
- moveState.canPauseAfter = true;
- NewSingleSegmentMoveAvailable();
+ ms.feedRate = ms.currentTool->GetUnRetractSpeed() * ms.currentTool->DriveCount();
+ ms.filePos = gb.GetJobFilePosition();
+ ms.canPauseAfter = true;
+ NewSingleSegmentMoveAvailable(ms);
}
gb.SetState(GCodeState::normal);
}
@@ -1384,9 +1399,9 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::loadingFilament:
// We just returned from the filament load macro
- if (reprap.GetCurrentTool() != nullptr)
+ if (ms.currentTool != nullptr)
{
- reprap.GetCurrentTool()->GetFilament()->Load(filamentToLoad);
+ ms.currentTool->GetFilament()->Load(filamentToLoad);
if (reprap.Debug(moduleGcodes))
{
platform.MessageF(LoggedGenericMessage, "Filament %s loaded", filamentToLoad);
@@ -1397,13 +1412,13 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::unloadingFilament:
// We just returned from the filament unload macro
- if (reprap.GetCurrentTool() != nullptr)
+ if (ms.currentTool != nullptr)
{
if (reprap.Debug(moduleGcodes))
{
- platform.MessageF(LoggedGenericMessage, "Filament %s unloaded", reprap.GetCurrentTool()->GetFilament()->GetName());
+ platform.MessageF(LoggedGenericMessage, "Filament %s unloaded", ms.currentTool->GetFilament()->GetName());
}
- reprap.GetCurrentTool()->GetFilament()->Unload();
+ ms.currentTool->GetFilament()->Unload();
}
gb.SetState(GCodeState::normal);
break;
@@ -1513,11 +1528,11 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
{
gb.SetState(GCodeState::finishedProcessingEvent); // already paused or pausing
}
- else if (LockMovementAndWaitForStandstill(gb))
+ else
{
const PrintPausedReason pauseReason = Event::GetDefaultPauseReason();
- gb.SetState(GCodeState::finishedProcessingEvent);
- DoPause(gb, pauseReason, (pauseReason == PrintPausedReason::driverError) ? GCodeState::eventPausing2 : GCodeState::eventPausing1);
+ // In the following, if DoPause fails because it can't get the movement lock then it will not change the state, so we will return here to try again
+ (void)DoAsynchronousPause(gb, pauseReason, (pauseReason == PrintPausedReason::driverError) ? GCodeState::eventPausing2 : GCodeState::eventPausing1);
}
}
break;
@@ -1539,7 +1554,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// We completed a command, so unlock resources and tell the host about it
gb.StopTimer();
UnlockAll(gb);
- gb.LatestMachineState().RetrieveStateMachineResult(stateMachineResult, reply);
+ gb.LatestMachineState().RetrieveStateMachineResult(gb, reply, stateMachineResult);
HandleReply(gb, stateMachineResult, reply.c_str());
CheckForDeferredPause(gb);
@@ -1548,7 +1563,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
#if HAS_SBC_INTERFACE
if (reportPause)
{
- fileGCode->Invalidate();
+ FileGCode()->Invalidate();
reprap.GetSbcInterface().ReportPause();
}
#endif
diff --git a/src/GCodes/GCodes5.cpp b/src/GCodes/GCodes5.cpp
new file mode 100644
index 00000000..833b7ff7
--- /dev/null
+++ b/src/GCodes/GCodes5.cpp
@@ -0,0 +1,356 @@
+/*
+ * GCodes5.cpp
+ *
+ * Created on: 8 Mar 2022
+ * Author: David
+ *
+ * Purpose: Tool management
+ */
+
+#include "GCodes.h"
+#include "GCodeBuffer/GCodeBuffer.h"
+#include <Tools/Tool.h>
+#include <Platform/RepRap.h>
+#include <Heating/Heat.h>
+
+// Check if the specified heater is used by a current tool other than the specified one
+bool GCodes::IsHeaterUsedByDifferentCurrentTool(int heaterNumber, const Tool *tool) const noexcept
+{
+ for (const MovementState& ms : moveStates)
+ {
+ if (ms.currentTool != nullptr && ms.currentTool != tool && ms.currentTool->UsesHeater(heaterNumber))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Report the temperatures of one tool in M105 format
+void GCodes::ReportToolTemperatures(const StringRef& reply, const Tool *tool, bool includeNumber) const noexcept
+{
+ if (tool != nullptr && tool->HeaterCount() != 0)
+ {
+ if (reply.strlen() != 0)
+ {
+ reply.cat(' ');
+ }
+ if (includeNumber)
+ {
+ reply.catf("T%u", tool->Number());
+ }
+ else
+ {
+ reply.cat("T");
+ }
+
+ Heat& heat = reprap.GetHeat();
+ char sep = ':';
+ for (size_t i = 0; i < tool->HeaterCount(); ++i)
+ {
+ const int heater = tool->GetHeater(i);
+ reply.catf("%c%.1f /%.1f", sep, (double)heat.GetHeaterTemperature(heater), (double)heat.GetTargetTemperature(heater));
+ sep = ' ';
+ }
+ }
+}
+
+#if SUPPORT_ASYNC_MOVES
+
+// Handle M596
+GCodeResult GCodes::SelectMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
+{
+ if (gb.Seen('P'))
+ {
+ UnlockMovement(gb); // in case we are in a macro - avoid unlocking the wrong movement system later
+ const unsigned int queueNumber = gb.GetLimitedUIValue('P', ARRAY_SIZE(moveStates));
+ gb.SetActiveQueueNumber(queueNumber);
+ reprap.InputsUpdated();
+ }
+ else
+ {
+ reply.printf("Motion system %u is active", gb.GetActiveQueueNumber());
+ }
+ return GCodeResult::ok;
+}
+
+// Handle M597
+GCodeResult GCodes::CollisionAvoidance(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
+{
+ // Find the two specified axes
+ int lowerAxisNumber = -1, upperAxisNumber = -1;
+ float lowerValue, upperValue;
+ for (unsigned int i = 0; i < numVisibleAxes; ++i)
+ {
+ if (gb.Seen(axisLetters[i]))
+ {
+ if (lowerAxisNumber < 0)
+ {
+ lowerAxisNumber = i;
+ lowerValue = gb.GetFValue();
+ }
+ else
+ {
+ upperAxisNumber = i;
+ upperValue = gb.GetFValue();
+ break;
+ }
+ }
+ }
+
+ if (upperAxisNumber >= 0)
+ {
+ // Seen two axes, so go ahead
+ if (upperValue == lowerValue)
+ {
+ reply.copy("Axis values must be different");
+ return GCodeResult::error;
+ }
+ if (upperValue < lowerValue)
+ {
+ std::swap(upperValue, lowerValue);
+ std::swap(upperAxisNumber, lowerAxisNumber);
+ collisionChecker.Set(lowerAxisNumber, upperAxisNumber, upperValue - lowerValue, GetMovementState(gb).coords);
+ }
+ }
+ else if (lowerAxisNumber >= 0)
+ {
+ reply.copy("Only one axis specified");
+ return GCodeResult::error;
+ }
+ else if (collisionChecker.IsValid())
+ {
+ reply.printf("For collision avoidance, axis %c position must be at least %.1fmm higher than axis %c",
+ axisLetters[collisionChecker.GetUpperAxis()], (double)collisionChecker.GetMinSeparation(), axisLetters[collisionChecker.GetLowerAxis()]);
+ }
+ else
+ {
+ reply.copy("Collision avoidance is not active");
+ }
+ return GCodeResult::ok;
+}
+
+#endif
+
+GCodeResult GCodes::HandleM486(GCodeBuffer &gb, const StringRef &reply, OutputBuffer*& buf) THROWS(GCodeException)
+{
+ bool seen = false;
+ if (gb.Seen('T'))
+ {
+ // Specify how many objects. May be useful for a user interface.
+ seen = true;
+ buildObjects.HandleM486T(gb.GetUIValue());
+ }
+
+ if (gb.Seen('S'))
+ {
+ // Specify which object we are about to print
+ seen = true;
+ buildObjects.UseM486Labelling();
+
+ const int num = gb.GetIValue();
+ if (num >= 0 && num < (int)MaxTrackedObjects && gb.Seen('A'))
+ {
+ String<StringLength50> objectName;
+ gb.GetQuotedString(objectName.GetRef());
+ buildObjects.SetM486Label(num, objectName.c_str());
+ }
+ ChangeToObject(gb, num);
+ }
+
+ const bool seenC = gb.Seen('C');
+ if (seenC || gb.Seen('P'))
+ {
+ // Cancel an object
+ seen = true;
+ const int objectToCancel = (seenC) ? GetMovementState(gb).currentObjectNumber : (int)gb.GetUIValue();
+ if (objectToCancel < 0)
+ {
+ reply.copy("No current object");
+ return GCodeResult::error;
+ }
+
+ if (buildObjects.CancelObject((unsigned int)objectToCancel))
+ {
+ for (MovementState& ms : moveStates)
+ {
+ if (objectToCancel == ms.currentObjectNumber)
+ {
+ ms.StopPrinting(gb);
+ }
+ }
+ reply.printf("Object %d cancelled", objectToCancel);
+ }
+ }
+
+ if (gb.Seen('U'))
+ {
+ // Resume an object
+ seen = true;
+ const unsigned int objectToResume = gb.GetUIValue();
+ if (buildObjects.ResumeObject(objectToResume))
+ {
+ for (MovementState& ms : moveStates)
+ {
+ if ((int)objectToResume == ms.currentObjectNumber)
+ {
+ ms.ResumePrinting(gb);
+ }
+ }
+ reprap.JobUpdated();
+ }
+ }
+
+ if (!seen)
+ {
+ // List objects on build plate
+ if (!OutputBuffer::Allocate(buf))
+ {
+ return GCodeResult::notFinished;
+ }
+
+ buildObjects.ListObjects(buf);
+ }
+
+ return GCodeResult::ok;
+}
+
+// This is called when we have found an object label in a comment
+void GCodes::StartObject(GCodeBuffer& gb, const char *_ecv_array label) noexcept
+{
+ if (!buildObjects.IsUsingM486Naming())
+ {
+ const size_t objectNumber = buildObjects.GetObjectNumber(label);
+ ChangeToObject(gb, objectNumber);
+ }
+}
+
+// This is called when we have found a "stop printing object" comment
+void GCodes::StopObject(GCodeBuffer& gb) noexcept
+{
+ if (!buildObjects.IsUsingM486Naming())
+ {
+ ChangeToObject(gb, -1);
+ }
+}
+
+void GCodes::ChangeToObject(GCodeBuffer& gb, int objectNumber) noexcept
+{
+ MovementState& ms = GetMovementState(gb);
+ ms.currentObjectNumber = objectNumber;
+ const bool cancelCurrentObject = buildObjects.CheckObject(objectNumber);
+ if (cancelCurrentObject && !ms.currentObjectCancelled)
+ {
+ ms.StopPrinting(gb);
+ }
+ else if (!cancelCurrentObject && ms.currentObjectCancelled)
+ {
+ ms.ResumePrinting(gb);
+ }
+}
+
+// Process M204
+GCodeResult GCodes::ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException)
+{
+ MovementState& ms = GetMovementState(gb);
+ bool seen = false;
+ if (gb.Seen('S'))
+ {
+ // For backwards compatibility with old versions of Marlin (e.g. for Cura and the Prusa fork of slic3r), set both accelerations
+ seen = true;
+ ms.maxTravelAcceleration = ms.maxPrintingAcceleration = gb.GetAcceleration();
+ }
+ if (gb.Seen('P'))
+ {
+ seen = true;
+ ms.maxPrintingAcceleration = gb.GetAcceleration();
+ }
+ if (gb.Seen('T'))
+ {
+ seen = true;
+ ms.maxTravelAcceleration = gb.GetAcceleration();
+ }
+ if (seen)
+ {
+ reprap.MoveUpdated();
+ }
+ else
+ {
+ reply.printf("Maximum printing acceleration %.1f, maximum travel acceleration %.1f mm/sec^2",
+ (double)InverseConvertAcceleration(ms.maxPrintingAcceleration), (double)InverseConvertAcceleration(ms.maxTravelAcceleration));
+ }
+ return GCodeResult::ok;
+}
+
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
+
+// Save some resume information, returning true if successful
+// We assume that the tool configuration doesn't change, only the temperatures and the mix
+bool GCodes::WriteToolSettings(FileStore *f, const MovementState& ms) const noexcept
+{
+ // First write the settings of all tools except the current one and the command to select them if they are on standby
+ bool ok = true;
+ ReadLocker lock(Tool::toolListLock);
+ for (const Tool *t = Tool::GetToolList(); t != nullptr && ok; t = t->Next())
+ {
+ if (t != ms.currentTool)
+ {
+ ok = t->WriteSettings(f);
+ }
+ }
+
+ // Finally write the settings of the active tool and the commands to select it. If no current tool, just deselect all tools.
+ if (ok)
+ {
+ if (ms.currentTool == nullptr)
+ {
+ ok = f->Write("T-1 P0\n");
+ }
+ else
+ {
+ ok = ms.currentTool->WriteSettings(f);
+ if (ok)
+ {
+ String<StringLength20> buf;
+ buf.printf("T%u P0\n", ms.currentTool->Number());
+ ok = f->Write(buf.c_str());
+ }
+ }
+ }
+ return ok;
+}
+
+// Save some information in config-override.g
+bool GCodes::WriteToolParameters(FileStore *f, const bool forceWriteOffsets) const noexcept
+{
+ bool ok = true, written = false;
+ ReadLocker lock(Tool::toolListLock);
+ for (const Tool *t = Tool::GetToolList(); ok && t != nullptr; t = t->Next())
+ {
+ const AxesBitmap axesProbed = t->GetAxisOffsetsProbed();
+ if (axesProbed.IsNonEmpty() || forceWriteOffsets)
+ {
+ String<StringLength256> scratchString;
+ if (!written)
+ {
+ scratchString.copy("; Probed tool offsets\n");
+ written = true;
+ }
+ scratchString.catf("G10 P%d", t->Number());
+ for (size_t axis = 0; axis < MaxAxes; ++axis)
+ {
+ if (forceWriteOffsets || axesProbed.IsBitSet(axis))
+ {
+ scratchString.catf(" %c%.2f", GetAxisLetters()[axis], (double)(t->GetOffset(axis)));
+ }
+ }
+ scratchString.cat('\n');
+ ok = f->Write(scratchString.c_str());
+ }
+ }
+ return ok;
+}
+
+#endif
+
+// End
diff --git a/src/GCodes/GCodes6.cpp b/src/GCodes/GCodes6.cpp
new file mode 100644
index 00000000..cb330028
--- /dev/null
+++ b/src/GCodes/GCodes6.cpp
@@ -0,0 +1,458 @@
+/*
+ * GCodes6.cpp
+ *
+ * Created on: 19 Jul 2022
+ * Author: David
+ *
+ * This file defined member functions of the GCodes class that handle bed probing and height maps
+ */
+
+#include "GCodes.h"
+#include "GCodeBuffer/GCodeBuffer.h"
+#include <Movement/Move.h>
+#include <Endstops/ZProbe.h>
+
+// This is called to execute a G30.
+// It sets wherever we are as the probe point P (probePointIndex) then probes the bed, or gets all its parameters from the arguments.
+// If X or Y are specified, use those; otherwise use the machine's coordinates. If no Z is specified use the machine's coordinates.
+// If it is specified and is greater than SILLY_Z_VALUE (i.e. greater than -9999.0) then that value is used.
+// If it's less than SILLY_Z_VALUE the bed is probed and that value is used.
+// We already own the movement lock before this is called.
+GCodeResult GCodes::ExecuteG30(GCodeBuffer& gb, const StringRef& reply)
+{
+ g30SValue = (gb.Seen('S')) ? gb.GetIValue() : -4; // S-4 or lower is equivalent to having no S parameter
+ const MovementState& ms = GetMovementState(gb);
+ if (g30SValue == -2 && ms.currentTool == nullptr)
+ {
+ reply.copy("G30 S-2 commanded with no tool selected");
+ return GCodeResult::error;
+ }
+
+ g30HValue = (gb.Seen('H')) ? gb.GetFValue() : 0.0;
+ g30ProbePointIndex = -1;
+ bool seenP = false;
+ gb.TryGetIValue('P', g30ProbePointIndex, seenP);
+ if (seenP)
+ {
+ if (g30ProbePointIndex < 0 || g30ProbePointIndex >= (int)MaxProbePoints)
+ {
+ reply.copy("Z probe point index out of range");
+ return GCodeResult::error;
+ }
+ else
+ {
+ // Set the specified probe point index to the specified coordinates
+ const float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : ms.currentUserPosition[X_AXIS];
+ const float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : ms.currentUserPosition[Y_AXIS];
+ const float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : ms.currentUserPosition[Z_AXIS];
+ reprap.GetMove().SetXYBedProbePoint((size_t)g30ProbePointIndex, x, y);
+
+ if (z > SILLY_Z_VALUE)
+ {
+ // Just set the height error to the specified Z coordinate
+ reprap.GetMove().SetZBedProbePoint((size_t)g30ProbePointIndex, z, false, false);
+ if (g30SValue >= -1)
+ {
+ return GetGCodeResultFromError(reprap.GetMove().FinishedBedProbing(g30SValue, reply));
+ }
+ }
+ else
+ {
+ // Do a Z probe at the specified point.
+ const auto zp = SetZProbeNumber(gb, 'K'); // may throw, so do this before changing the state
+ gb.SetState(GCodeState::probingAtPoint0);
+ if (zp->GetProbeType() != ZProbeType::blTouch)
+ {
+ DeployZProbe(gb);
+ }
+ }
+ }
+ }
+ else
+ {
+ // G30 without P parameter. This probes the current location starting from the current position.
+ // If S=-1 it just reports the stopped height, else it resets the Z origin.
+ const auto zp = SetZProbeNumber(gb, 'K'); // may throw, so do this before changing the state
+ InitialiseTaps(zp->HasTwoProbingSpeeds());
+ gb.SetState(GCodeState::probingAtPoint2a);
+ if (zp->GetProbeType() != ZProbeType::blTouch)
+ {
+ DeployZProbe(gb);
+ }
+ }
+ return GCodeResult::ok;
+}
+
+// Set up currentZProbeNumber and return the probe
+ReadLockedPointer<ZProbe> GCodes::SetZProbeNumber(GCodeBuffer& gb, char probeLetter) THROWS(GCodeException)
+{
+ const uint32_t probeNumber = (gb.Seen(probeLetter)) ? gb.GetLimitedUIValue(probeLetter, MaxZProbes) : 0;
+ auto zp = reprap.GetPlatform().GetEndstops().GetZProbe(probeNumber);
+ if (zp.IsNull())
+ {
+ gb.ThrowGCodeException("Z probe %u not found", probeNumber);
+ }
+ currentZProbeNumber = (uint8_t)probeNumber;
+ return zp;
+}
+
+// Decide which device to display a message box on
+MessageType GCodes::GetMessageBoxDevice(GCodeBuffer& gb) const
+{
+ MessageType mt = gb.GetResponseMessageType();
+ if (mt == GenericMessage)
+ {
+ // Command source was the file being printed, or a trigger. Send the message to PanelDue if there is one, else to the web server.
+ mt = (lastAuxStatusReportType >= 0) ? AuxMessage : HttpMessage;
+ }
+ return mt;
+}
+
+void GCodes::DoManualProbe(GCodeBuffer& gb, const char *message, const char *title, const AxesBitmap axes)
+{
+ if (Push(gb, true)) // stack the machine state including the file position and set the state to GCodeState::normal
+ {
+ gb.WaitForAcknowledgement(); // flag that we are waiting for acknowledgement
+ const MessageType mt = GetMessageBoxDevice(gb);
+ reprap.SendAlert(mt, message, title, 2, 0.0, axes);
+ }
+}
+
+// Do a manual bed probe. On entry the state variable is the state we want to return to when the user has finished adjusting the height.
+void GCodes::DoManualBedProbe(GCodeBuffer& gb)
+{
+ DoManualProbe(gb, "Adjust height until the nozzle just touches the bed, then press OK", "Manual bed probing", AxesBitmap::MakeFromBits(Z_AXIS));
+}
+
+// Define the probing grid, called when we see an M557 command
+GCodeResult GCodes::DefineGrid(GCodeBuffer& gb, const StringRef &reply) THROWS(GCodeException)
+{
+ if (!LockMovement(gb)) // to ensure that probing is not already in progress
+ {
+ return GCodeResult::notFinished;
+ }
+
+ bool seenR = false, seenP = false, seenS = false;
+ char axesLetters[2] = { 'X', 'Y'};
+ float axis0Values[2];
+ float axis1Values[2];
+ float spacings[2] = { DefaultGridSpacing, DefaultGridSpacing };
+
+ size_t axesSeenCount = 0;
+ for (size_t axis = 0; axis < numVisibleAxes; axis++)
+ {
+ if (gb.Seen(axisLetters[axis]))
+ {
+ if (axisLetters[axis] == 'Z')
+ {
+ reply.copy("Z axis is not allowed for mesh leveling");
+ return GCodeResult::error;
+ }
+ else if (axesSeenCount > 2)
+ {
+ reply.copy("Mesh leveling expects exactly two axes");
+ return GCodeResult::error;
+ }
+ bool dummy;
+ gb.TryGetFloatArray(axisLetters[axis], 2, (axesSeenCount == 0) ? axis0Values : axis1Values, dummy, false);
+ axesLetters[axesSeenCount] = axisLetters[axis];
+ ++axesSeenCount;
+ }
+ }
+ if (axesSeenCount == 1)
+ {
+ reply.copy("Specify zero or two axes in M557");
+ return GCodeResult::error;
+ }
+ const bool axesSeen = axesSeenCount > 0;
+
+ uint32_t numPoints[2];
+ gb.TryGetUIArray('P', 2, numPoints, seenP, true);
+ if (!seenP)
+ {
+ gb.TryGetFloatArray('S', 2, spacings, seenS, true);
+ }
+
+ float radius = -1.0;
+ gb.TryGetFValue('R', radius, seenR);
+
+ if (!axesSeen && !seenR && !seenS && !seenP)
+ {
+ // Just print the existing grid parameters
+ if (defaultGrid.IsValid())
+ {
+ reply.copy("Grid: ");
+ defaultGrid.PrintParameters(reply);
+ }
+ else
+ {
+ reply.copy("Grid is not defined");
+ }
+ return GCodeResult::ok;
+ }
+
+ if (!axesSeen && !seenR)
+ {
+ // Must have given just the S or P parameter
+ reply.copy("specify at least radius or two axis ranges in M557");
+ return GCodeResult::error;
+ }
+
+ if (axesSeen)
+ {
+ // Seen both axes
+ if (seenP)
+ {
+ // In the following, we multiply the spacing by 0.9999 to ensure that when we divide the axis range by the spacing, we get the correct number of points
+ // Otherwise, for some values we occasionally get one less point
+ if (spacings[0] >= 2 && axis0Values[1] > axis0Values[0])
+ {
+ spacings[0] = (axis0Values[1] - axis0Values[0])/(numPoints[0] - 1) * 0.9999;
+ }
+ if (spacings[1] >= 2 && axis1Values[1] > axis1Values[0])
+ {
+ spacings[1] = (axis1Values[1] - axis1Values[0])/(numPoints[1] - 1) * 0.9999;
+ }
+ }
+ }
+ else
+ {
+ // Seen R
+ if (radius > 0.0)
+ {
+ float effectiveXRadius;
+ if (seenP && numPoints[0] >= 2)
+ {
+ effectiveXRadius = radius - 0.1;
+ if (numPoints[1] % 2 == 0)
+ {
+ effectiveXRadius *= fastSqrtf(1.0 - 1.0/(float)((numPoints[1] - 1) * (numPoints[1] - 1)));
+ }
+ spacings[0] = (2 * effectiveXRadius)/(numPoints[0] - 1);
+ }
+ else
+ {
+ effectiveXRadius = floorf((radius - 0.1)/spacings[0]) * spacings[0];
+ }
+ axis0Values[0] = -effectiveXRadius;
+ axis0Values[1] = effectiveXRadius + 0.1;
+
+ float effectiveYRadius;
+ if (seenP && numPoints[1] >= 2)
+ {
+ effectiveYRadius = radius - 0.1;
+ if (numPoints[0] % 2 == 0)
+ {
+ effectiveYRadius *= fastSqrtf(1.0 - 1.0/(float)((numPoints[0] - 1) * (numPoints[0] - 1)));
+ }
+ spacings[1] = (2 * effectiveYRadius)/(numPoints[1] - 1);
+ }
+ else
+ {
+ effectiveYRadius = floorf((radius - 0.1)/spacings[1]) * spacings[1];
+ }
+ axis1Values[0] = -effectiveYRadius;
+ axis1Values[1] = effectiveYRadius + 0.1;
+ }
+ else
+ {
+ reply.copy("M577 radius must be positive unless X and Y are specified");
+ return GCodeResult::error;
+ }
+ }
+
+ const bool ok = defaultGrid.Set(axesLetters, axis0Values, axis1Values, radius, spacings);
+ reprap.MoveUpdated();
+ if (ok)
+ {
+ return GCodeResult::ok;
+ }
+
+ const float axis1Range = axesSeen ? axis0Values[1] - axis0Values[0] : 2 * radius;
+ const float axis2Range = axesSeen ? axis1Values[1] - axis1Values[0] : 2 * radius;
+ reply.copy("bad grid definition: ");
+ defaultGrid.PrintError(axis1Range, axis2Range, reply);
+ return GCodeResult::error;
+}
+
+// Start probing the grid, returning true if we didn't because of an error.
+// Prior to calling this the movement system must be locked.
+GCodeResult GCodes::ProbeGrid(GCodeBuffer& gb, const StringRef& reply)
+{
+ if (!defaultGrid.IsValid())
+ {
+ reply.copy("No valid grid defined for bed probing");
+ return GCodeResult::error;
+ }
+
+ if (!AllAxesAreHomed())
+ {
+ reply.copy("Must home printer before bed probing");
+ return GCodeResult::error;
+ }
+
+ const auto zp = SetZProbeNumber(gb, 'K'); // may throw, so do this before changing the state
+
+#if SUPPORT_PROBE_POINTS_FILE
+ if (!probePointsFileLoaded) // if we are using a probe points file then the grid has already been loaded
+#endif
+ {
+ reprap.GetMove().AccessHeightMap().SetGrid(defaultGrid);
+ }
+
+ ClearBedMapping();
+ gridAxis0index = gridAxis1index = 0;
+
+ gb.SetState(GCodeState::gridProbing1);
+ if (zp->GetProbeType() != ZProbeType::blTouch)
+ {
+ DeployZProbe(gb);
+ }
+ return GCodeResult::ok;
+}
+
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
+
+GCodeResult GCodes::LoadHeightMap(GCodeBuffer& gb, const StringRef& reply)
+{
+ ClearBedMapping();
+
+ String<MaxFilenameLength> heightMapFileName;
+ bool seen = false;
+ gb.TryGetQuotedString('P', heightMapFileName.GetRef(), seen);
+ if (!seen)
+ {
+ heightMapFileName.copy(DefaultHeightMapFile);
+ }
+
+ String<MaxFilenameLength> fullName;
+ platform.MakeSysFileName(fullName.GetRef(), heightMapFileName.c_str());
+ FileStore * const f = MassStorage::OpenFile(fullName.c_str(), OpenMode::read, 0);
+ if (f == nullptr)
+ {
+ reply.printf("Height map file %s not found", fullName.c_str());
+ return GCodeResult::error;
+ }
+ reply.printf("Failed to load height map from file %s: ", fullName.c_str()); // set up error message to append to
+
+ const bool err = reprap.GetMove().LoadHeightMapFromFile(f, fullName.c_str(), reply);
+ f->Close();
+
+ ActivateHeightmap(!err);
+ if (err)
+ {
+ return GCodeResult::error;
+ }
+
+ reply.Clear(); // get rid of the error message
+ if (!zDatumSetByProbing && platform.GetZProbeOrDefault(0)->GetProbeType() != ZProbeType::none) //TODO store Z probe number in height map
+ {
+ reply.copy("the height map was loaded when the current Z=0 datum was not determined by probing. This may result in a height offset.");
+ return GCodeResult::warning;
+ }
+
+ return GCodeResult::ok;
+}
+
+// Save the height map and append the success or error message to 'reply', returning true if an error occurred
+bool GCodes::TrySaveHeightMap(const char *filename, const StringRef& reply) const noexcept
+{
+ String<MaxFilenameLength> fullName;
+ platform.MakeSysFileName(fullName.GetRef(), filename);
+ FileStore * const f = MassStorage::OpenFile(fullName.c_str(), OpenMode::write, 0);
+ bool err;
+ if (f == nullptr)
+ {
+ reply.catf("Failed to create height map file %s", fullName.c_str());
+ err = true;
+ }
+ else
+ {
+ err = reprap.GetMove().SaveHeightMapToFile(f, fullName.c_str());
+ f->Close();
+ if (err)
+ {
+ MassStorage::Delete(fullName.c_str(), false);
+ reply.catf("Failed to save height map to file %s", fullName.c_str());
+ }
+ else
+ {
+ reply.catf("Height map saved to file %s", fullName.c_str());
+ }
+ }
+ return err;
+}
+
+// Save the height map to the file specified by P parameter
+GCodeResult GCodes::SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const THROWS(GCodeException)
+{
+ // No need to check if we're using the SBC interface here, because TrySaveHeightMap does that already
+ if (gb.Seen('P'))
+ {
+ String<MaxFilenameLength> heightMapFileName;
+ gb.GetQuotedString(heightMapFileName.GetRef());
+ return GetGCodeResultFromError(TrySaveHeightMap(heightMapFileName.c_str(), reply));
+ }
+ return GetGCodeResultFromError(TrySaveHeightMap(DefaultHeightMapFile, reply));
+}
+
+# if SUPPORT_PROBE_POINTS_FILE
+
+// Load map of reachable probe points from file
+GCodeResult GCodes::LoadProbePointsMap(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
+{
+ ClearBedMapping();
+ ClearProbePointsInvalid();
+
+ String<MaxFilenameLength> probePointsFileName;
+ bool seen = false;
+ gb.TryGetQuotedString('P', probePointsFileName.GetRef(), seen);
+ if (!seen)
+ {
+ probePointsFileName.copy(DefaultProbeProbePointsFile);
+ }
+
+ String<MaxFilenameLength> fullName;
+ platform.MakeSysFileName(fullName.GetRef(), probePointsFileName.c_str());
+ FileStore * const f = MassStorage::OpenFile(fullName.c_str(), OpenMode::read, 0);
+ if (f == nullptr)
+ {
+ reply.printf("Probe points file %s not found", fullName.c_str());
+ return GCodeResult::error;
+ }
+
+ reply.printf("Failed to load probe points from file %s: ", fullName.c_str()); // set up error message to append to
+ const bool err = reprap.GetMove().LoadProbePointsFromFile(f, fullName.c_str(), reply);
+ f->Close();
+ if (err)
+ {
+ return GCodeResult::error;
+ }
+
+ reply.Clear(); // get rid of the error message
+ probePointsFileLoaded = true; // use the grid we loaded when we probe
+ return GCodeResult::ok;
+}
+
+void GCodes::ClearProbePointsInvalid() noexcept
+{
+ reprap.GetMove().ClearProbePointsInvalid();
+ probePointsFileLoaded = false; // use the default grid when we next probe
+}
+
+#endif // SUPPORT_PROBE_POINTS_FILE
+
+#endif // HAS_MASS_STORAGE || HAS_SBC_INTERFACE
+
+// Stop using bed compensation
+void GCodes::ClearBedMapping() noexcept
+{
+ reprap.GetMove().SetIdentityTransform();
+ for (MovementState& ms : moveStates)
+ {
+ reprap.GetMove().GetCurrentUserPosition(ms.coords, 0, ms.currentTool);
+ ToolOffsetInverseTransform(ms); // update user coordinates to remove any height map offset there was at the current position
+ }
+}
+
+// End
diff --git a/src/GCodes/GCodes7.cpp b/src/GCodes/GCodes7.cpp
new file mode 100644
index 00000000..171ef8bf
--- /dev/null
+++ b/src/GCodes/GCodes7.cpp
@@ -0,0 +1,163 @@
+/*
+ * GCodes7.cpp
+ *
+ * Created on: 14 Sept 2022
+ * Author: David
+ *
+ * This file handles M291, M292 and other functions related to message boxes
+ */
+
+#include "GCodes.h"
+#include "GCodeBuffer/GCodeBuffer.h"
+
+// Process M291
+GCodeResult GCodes::DoMessageBox(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException)
+{
+ // Get the message
+ gb.MustSee('P');
+ String<MaxMessageLength> message;
+ gb.GetQuotedString(message.GetRef());
+
+ // Get the optional message box title
+ bool dummy = false;
+ String<StringLength100> title;
+ (void)gb.TryGetQuotedString('R', title.GetRef(), dummy);
+
+ // Get the message box mode
+ uint32_t sParam = 1;
+ (void)gb.TryGetLimitedUIValue('S', sParam, dummy, 8);
+
+ // Get the optional timeout parameter. The default value depends on the mode (S parameter).
+ float tParam = (sParam <= 1) ? DefaultMessageTimeout : 0.0;
+ gb.TryGetNonNegativeFValue('T', tParam, dummy);
+
+ MessageBoxLimits limits;
+ gb.TryGetBValue('J', limits.canCancel, dummy);
+
+ AxesBitmap axisControls;
+
+ switch (sParam)
+ {
+ case 0: // no buttons displayed, non-blocking
+ if (tParam <= 0.0)
+ {
+ reply.copy("Attempt to create a message box that cannot be dismissed");
+ return GCodeResult::error;
+ }
+ break;
+
+ case 1: // Close button displayed, non-blocking
+ default:
+ break;
+
+ case 2: // OK button displayed, blocking
+ case 3: // OK and Cancel buttons displayed, blocking
+ // Message box modes 2 and 3 can take a list of axes that can be jogged
+ for (size_t axis = 0; axis < numTotalAxes; axis++)
+ {
+ if (gb.Seen(axisLetters[axis]) && gb.GetIValue() > 0)
+ {
+ axisControls.SetBit(axis);
+ }
+ }
+ break;
+
+ case 4: // Multiple choices, blocking
+ gb.MustSee('K');
+ limits.choices = gb.GetExpression();
+ if (limits.choices.IsHeapStringArrayType())
+ {
+ uint32_t defaultChoice = 0;
+ if (gb.TryGetUIValue('F', defaultChoice, dummy))
+ {
+ limits.defaultVal.SetInt((int32_t)defaultChoice);
+ }
+ break;
+ }
+ reply.copy("K parameter must be an array of strings");
+ return GCodeResult::error;
+
+ case 5: // Integer value required, blocking
+ limits.GetIntegerLimits(gb, false);
+ break;
+
+ case 6: // Floating point value required, blocking
+ limits.GetFloatLimits(gb);
+ break;
+
+ case 7: // String value required, blocking
+ limits.GetIntegerLimits(gb, true);
+ break;
+ }
+
+ if (sParam >= 2) // if it's a blocking message box
+ {
+ // Don't lock the movement system, because if we do then only the channel that issues the M291 can move the axes
+#if HAS_SBC_INTERFACE
+ if (reprap.UsingSbcInterface())
+ {
+ gb.SetState(GCodeState::waitingForAcknowledgement);
+ }
+#endif
+ if (Push(gb, true)) // stack the machine state including the file position
+ {
+ UnlockMovement(gb); // allow movement so that e.g. an SD card print can call M291 and then DWC or PanelDue can be used to jog axes
+ gb.WaitForAcknowledgement(); // flag that we are waiting for acknowledgement
+ }
+ }
+
+ // Display the message box on all relevant devices. Acknowledging any one of them clears them all.
+ const MessageType mt = GetMessageBoxDevice(gb); // get the display device
+ reprap.SendAlert(mt, message.c_str(), title.c_str(), (int)sParam, tParam, axisControls, &limits);
+ return GCodeResult::ok;
+}
+
+// Process M292
+GCodeResult GCodes::AcknowledgeMessage(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException)
+{
+ uint32_t seq = 0;
+ if (gb.Seen('S'))
+ {
+ seq = gb.GetUIValue();
+ }
+
+ const bool cancelled = (gb.Seen('P') && gb.GetIValue() == 1);
+ ExpressionValue rslt;
+ if (!cancelled && gb.Seen('R'))
+ {
+ rslt = gb.GetExpression();
+ }
+
+ bool wasBlocking;
+ if (reprap.AcknowledgeMessageBox(seq, wasBlocking))
+ {
+ if (wasBlocking)
+ {
+ MessageBoxClosed(cancelled, true, rslt);
+ }
+ return GCodeResult::ok;
+ }
+ else
+ {
+ reply.copy("no active message box");
+ return GCodeResult::error;
+ }
+}
+
+// Deal with processing a M292 or timing out a message box
+void GCodes::MessageBoxClosed(bool cancelled, bool m292, ExpressionValue rslt) noexcept
+{
+ platform.MessageF(MessageType::LogInfo,
+ "%s: cancelled=%s",
+ (m292) ? "M292" : "Message box timed out",
+ (cancelled ? "true" : "false"));
+ for (GCodeBuffer* targetGb : gcodeSources)
+ {
+ if (targetGb != nullptr)
+ {
+ targetGb->MessageAcknowledged(cancelled, rslt);
+ }
+ }
+}
+
+// End
diff --git a/src/GCodes/ObjectTracker.cpp b/src/GCodes/ObjectTracker.cpp
index 5db37dbc..38c02342 100644
--- a/src/GCodes/ObjectTracker.cpp
+++ b/src/GCodes/ObjectTracker.cpp
@@ -10,258 +10,162 @@
#include <Platform/RepRap.h>
#include "GCodes.h"
-#if TRACK_OBJECT_NAMES
-
// Object model tables and functions for class ObjectTracker
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(ObjectTracker, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(_condition,...) OBJECT_MODEL_FUNC_IF_BODY(ObjectTracker, _condition,__VA_ARGS__)
-constexpr ObjectModelArrayDescriptor ObjectTracker::objectsArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return min<size_t>(((const ObjectTracker*)self)->numObjects, MaxTrackedObjects); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 1); }
-};
-
-constexpr ObjectModelArrayDescriptor ObjectTracker::xArrayDescriptor =
+constexpr ObjectModelArrayTableEntry ObjectTracker::objectModelArrayTable[] =
{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ((const ObjectTracker*)self)->GetXCoordinate(context); }
+ // 0. objects
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return min<size_t>(((const ObjectTracker*)self)->numObjects, MaxTrackedObjects); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 1); }
+ },
+ // 1. X limits
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ((const ObjectTracker*)self)->GetXCoordinate(context); }
+ },
+ // 2. Y limits
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ((const ObjectTracker*)self)->GetYCoordinate(context); }
+ }
};
-constexpr ObjectModelArrayDescriptor ObjectTracker::yArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ((const ObjectTracker*)self)->GetYCoordinate(context); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(ObjectTracker)
constexpr ObjectModelTableEntry ObjectTracker::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. BuildObjects root
- { "currentObject", OBJECT_MODEL_FUNC((int32_t)self->currentObjectNumber), ObjectModelEntryFlags::live },
- { "m486Names", OBJECT_MODEL_FUNC(self->usingM486Naming), ObjectModelEntryFlags::none },
- { "m486Numbers", OBJECT_MODEL_FUNC(self->usingM486Labelling), ObjectModelEntryFlags::none },
- { "objects", OBJECT_MODEL_FUNC_NOSELF(&objectsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "currentObject", OBJECT_MODEL_FUNC_NOSELF((int32_t)reprap.GetGCodes().GetCurrentMovementState(context).currentObjectNumber), ObjectModelEntryFlags::live },
+ { "m486Names", OBJECT_MODEL_FUNC(self->usingM486Naming), ObjectModelEntryFlags::none },
+ { "m486Numbers", OBJECT_MODEL_FUNC(self->usingM486Labelling), ObjectModelEntryFlags::none },
+ { "objects", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
-#if TRACK_OBJECT_NAMES
// 1. ObjectDirectoryEntry root
- { "cancelled", OBJECT_MODEL_FUNC(self->IsCancelled(context.GetLastIndex())), ObjectModelEntryFlags::none },
- { "name", OBJECT_MODEL_FUNC(self->objectDirectory[context.GetLastIndex()].name.IncreaseRefCount()), ObjectModelEntryFlags::none },
- { "x", OBJECT_MODEL_FUNC_NOSELF(&xArrayDescriptor), ObjectModelEntryFlags::none },
- { "y", OBJECT_MODEL_FUNC_NOSELF(&yArrayDescriptor), ObjectModelEntryFlags::none },
-#endif
+ { "cancelled", OBJECT_MODEL_FUNC(self->IsCancelled(context.GetLastIndex())), ObjectModelEntryFlags::none },
+ { "name", OBJECT_MODEL_FUNC(self->objectDirectory[context.GetLastIndex()].name.IncreaseRefCount()), ObjectModelEntryFlags::none },
+ { "x", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
+ { "y", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::none },
};
constexpr uint8_t ObjectTracker::objectModelTableDescriptor[] =
{
- 1 + TRACK_OBJECT_NAMES, // number of sub-tables
+ 2, // number of sub-tables
4,
-#if TRACK_OBJECT_NAMES
4
-#endif
};
DEFINE_GET_OBJECT_MODEL_TABLE(ObjectTracker)
-#endif
-
void ObjectTracker::Init() noexcept
{
objectsCancelled.Clear();
- currentObjectNumber = -1;
numObjects = 0;
- currentObjectCancelled = printingJustResumed = usingM486Labelling = false;
-#if TRACK_OBJECT_NAMES
+ usingM486Labelling = false;
// Clear out all object names in case of late object model requests
for (ObjectDirectoryEntry& ode : objectDirectory)
{
ode.Init("");
}
usingM486Naming = false;
-#endif
}
-void ObjectTracker::ChangeToObject(GCodeBuffer& gb, int i) noexcept
+void ObjectTracker::HandleM486T(unsigned int p_numObjects) noexcept
{
- currentObjectNumber = i;
- if (currentObjectNumber >= (int)numObjects)
+ numObjects = p_numObjects;
+ objectsCancelled.Clear(); // assume this command is only used at the start of a print
+ reprap.JobUpdated();
+}
+
+void ObjectTracker::UseM486Labelling() noexcept
+{
+ if (!usingM486Labelling)
{
- numObjects = currentObjectNumber + 1;
+ usingM486Labelling = true;
+ reprap.JobUpdated();
}
- const bool cancelCurrentObject = currentObjectNumber >= 0 && currentObjectNumber < (int)objectsCancelled.MaxBits() && objectsCancelled.IsBitSet(currentObjectNumber);
- if (cancelCurrentObject && !currentObjectCancelled)
+}
+
+void ObjectTracker::SetM486Label(unsigned int objectNumber, const char *_ecv_array objectName) noexcept
+{
+ usingM486Naming = true;
+
+ if (objectNumber >= numObjects) // if this is a new object
{
- StopPrinting(gb);
+ CreateObject(objectNumber, objectName);
}
- else if (!cancelCurrentObject && currentObjectCancelled)
+ else if (objectNumber < MaxTrackedObjects && objectName[0] != 0 && strcmp(objectName, objectDirectory[objectNumber].name.Get().Ptr()) != 0)
{
- ResumePrinting(gb);
+ objectDirectory[objectNumber].SetName(objectName);
+ reprap.JobUpdated();
}
}
-GCodeResult ObjectTracker::HandleM486(GCodeBuffer &gb, const StringRef &reply, OutputBuffer*& buf) THROWS(GCodeException)
+// Cancel an object, returning true if it was not already cancelled
+bool ObjectTracker::CancelObject(unsigned int objectNumber) noexcept
{
- bool seen = false;
- if (gb.Seen('T'))
+ if (objectNumber < objectsCancelled.MaxBits() && !objectsCancelled.IsBitSet(objectNumber))
{
- // Specify how many objects. May be useful for a user interface.
- seen = true;
- numObjects = gb.GetUIValue();
- objectsCancelled.Clear(); // assume this command is only used at the start of a print
+ objectsCancelled.SetBit(objectNumber);
reprap.JobUpdated();
+ return true;
}
+ return false;
+}
- if (gb.Seen('S'))
+// Resume an object, returning true if it was not already enabled
+bool ObjectTracker::ResumeObject(unsigned int objectNumber) noexcept
+{
+ if (objectNumber < objectsCancelled.MaxBits() && objectsCancelled.IsBitSet(objectNumber))
{
- // Specify which object we are about to print
- seen = true;
- if (!usingM486Labelling)
- {
- usingM486Labelling = true;
- reprap.JobUpdated();
- }
- const int num = gb.GetIValue();
-#if TRACK_OBJECT_NAMES
- if (num >= 0 && num < (int)MaxTrackedObjects && gb.Seen('A'))
- {
- String<StringLength50> objectName;
- gb.GetQuotedString(objectName.GetRef());
- usingM486Naming = true;
-
- if (num >= (int)numObjects) // if this is a new object
- {
- CreateObject(num, objectName.c_str());
- }
- else if (num < (int)MaxTrackedObjects && strcmp(objectName.c_str(), objectDirectory[num].name.Get().Ptr()) != 0)
- {
- objectDirectory[num].SetName(objectName.c_str());
- reprap.JobUpdated();
- }
- }
-#endif
- ChangeToObject(gb, num);
+ objectsCancelled.ClearBit(objectNumber);
+ reprap.JobUpdated();
+ return true;
}
+ return false;
+}
- const bool seenC = gb.Seen('C');
- if (seenC || gb.Seen('P'))
+// Add the object number if it isn't already known, return true if it has been cancelled
+bool ObjectTracker::CheckObject(int objectNumber) noexcept
+{
+ if (objectNumber >= (int)numObjects)
{
- // Cancel an object
- seen = true;
- const int objectToCancel = (seenC) ? currentObjectNumber : (int)gb.GetUIValue();
- if (objectToCancel < 0)
- {
- reply.copy("No current object");
- return GCodeResult::error;
- }
- if (objectToCancel >= (int)objectsCancelled.MaxBits())
- {
- reply.copy("Object number out of range");
- return GCodeResult::error;
- }
-
- // We don't flag an error if you try to cancel the same object twice
- if (!objectsCancelled.IsBitSet(objectToCancel))
- {
- objectsCancelled.SetBit(objectToCancel);
- if (objectToCancel == currentObjectNumber)
- {
- StopPrinting(gb);
- }
- reprap.JobUpdated();
- reply.printf("Object %d cancelled", objectToCancel);
- }
+ numObjects = objectNumber + 1;
}
+ return objectNumber >= 0 && objectNumber < (int)objectsCancelled.MaxBits() && objectsCancelled.IsBitSet(objectNumber);
+}
- if (gb.Seen('U'))
+// List the objects on the build plate
+void ObjectTracker::ListObjects(OutputBuffer *buf) noexcept
+{
+ if (numObjects == 0)
{
- // Resume an object
- seen = true;
- const int objectToResume = gb.GetUIValue();
- if (objectToResume >= (int)objectsCancelled.MaxBits())
- {
- reply.copy("Object number out of range");
- return GCodeResult::error;
- }
-
- // We don't flag an error if you try to resume an object that is not cancelled
- if (objectsCancelled.IsBitSet(objectToResume))
- {
- objectsCancelled.ClearBit(objectToResume);
- if (objectToResume == currentObjectNumber)
- {
- ResumePrinting(gb);
- }
- reprap.JobUpdated();
- reply.printf("Object %d resumed", objectToResume);
- }
+ buf->copy("No known objects on build plate");
}
-
- if (!seen)
+ else
{
-#if TRACK_OBJECT_NAMES
- // List objects on build plate
- if (!OutputBuffer::Allocate(buf))
- {
- return GCodeResult::notFinished;
- }
-
- if (numObjects == 0)
- {
- buf->copy("No known objects on build plate");
- }
- else
+ for (size_t i = 0; i < min<unsigned int>(numObjects, MaxTrackedObjects); ++i)
{
- for (size_t i = 0; i < min<unsigned int>(numObjects, MaxTrackedObjects); ++i)
- {
- const ObjectDirectoryEntry& obj = objectDirectory[i];
- buf->lcatf("%2u%s: X %d to %dmm, Y %d to %dmm, %s",
- i,
- (objectsCancelled.IsBitSet(i) ? " (cancelled)" : ""),
- (int)obj.x[0], (int)obj.x[1],
- (int)obj.y[0], (int)obj.y[1],
- obj.name.Get().Ptr());
- }
- if (numObjects > MaxTrackedObjects)
- {
- buf->lcatf("%u more objects", numObjects - MaxTrackedObjects);
- }
+ const ObjectDirectoryEntry& obj = objectDirectory[i];
+ buf->lcatf("%2u%s: X %d to %dmm, Y %d to %dmm, %s",
+ i,
+ (objectsCancelled.IsBitSet(i) ? " (cancelled)" : ""),
+ (int)obj.x[0], (int)obj.x[1],
+ (int)obj.y[0], (int)obj.y[1],
+ obj.name.Get().Ptr());
}
-#else
- if (numObjects == 0)
+ if (numObjects > MaxTrackedObjects)
{
- reply.copy("No known objects on build plate");
+ buf->lcatf("%u more objects", numObjects - MaxTrackedObjects);
}
- else
- {
- reply.printf("%u objects on build plate", numObjects);
- }
-#endif
- }
-
- return GCodeResult::ok;
-}
-
-// We are currently printing, but we must now stop because the current object is cancelled
-void ObjectTracker::StopPrinting(GCodeBuffer& gb) noexcept
-{
- currentObjectCancelled = true;
- virtualToolNumber = reprap.GetCurrentToolNumber();
-}
-
-// We are currently not printing because the current object was cancelled, but now we need to print again
-void ObjectTracker::ResumePrinting(GCodeBuffer& gb) noexcept
-{
- currentObjectCancelled = false;
- printingJustResumed = true;
- reprap.GetGCodes().SavePosition(rp, gb); // save the position we should be at for the start of the next move
- if (reprap.GetCurrentToolNumber() != virtualToolNumber) // if the wrong tool is loaded
- {
- reprap.GetGCodes().StartToolChange(gb, virtualToolNumber, DefaultToolChangeParam);
}
}
@@ -273,20 +177,12 @@ bool ObjectTracker::WriteObjectDirectory(FileStore *f) const noexcept
bool ok = true;
// Write the object list
-#if TRACK_OBJECT_NAMES
for (size_t i = 0; ok && i < min<unsigned int>(numObjects, MaxTrackedObjects); ++i)
{
String<StringLength100> buf;
buf.printf("M486 S%u A\"%s\"\n", i, objectDirectory[i].name.Get().Ptr());
ok = f->Write(buf.c_str());
}
-#else
- {
- String<StringLength20> buf;
- buf.printf("M486 T%u", numObjects);
- ok = f->Write(buf.c_str());
- }
-#endif
if (ok)
{
@@ -303,7 +199,7 @@ bool ObjectTracker::WriteObjectDirectory(FileStore *f) const noexcept
if (ok)
{
String<StringLength20> buf;
- buf.printf("M486 S%d\n", currentObjectNumber);
+ buf.printf("M486 S%d\n", reprap.GetGCodes().GetPrimaryMovementState().currentObjectNumber);
ok = f->Write(buf.c_str());
}
@@ -312,8 +208,6 @@ bool ObjectTracker::WriteObjectDirectory(FileStore *f) const noexcept
#endif
-#if TRACK_OBJECT_NAMES
-
// Create a new entry in the object directory
void ObjectDirectoryEntry::Init(const char *label) noexcept
{
@@ -376,11 +270,11 @@ void ObjectDirectoryEntry::SetName(const char *label) noexcept
// Update the min and max object coordinates to include the coordinates passed
// We could pass both the start and end coordinates of the printing move, however it is simpler just to pass the end coordinates.
// This is OK because it is very unlikely that there won't be a subsequent extruding move that ends close to the original one.
-void ObjectTracker::UpdateObjectCoordinates(const float coords[], AxesBitmap axes) noexcept
+void ObjectTracker::UpdateObjectCoordinates(int objectNumber, const float coords[], AxesBitmap axes) noexcept
{
- if (currentObjectNumber >= 0 && currentObjectNumber < (int)MaxTrackedObjects)
+ if (objectNumber >= 0 && objectNumber < (int)MaxTrackedObjects)
{
- if (objectDirectory[currentObjectNumber].UpdateObjectCoordinates(coords, axes))
+ if (objectDirectory[objectNumber].UpdateObjectCoordinates(coords, axes))
{
reprap.JobUpdated();
}
@@ -403,55 +297,40 @@ void ObjectTracker::CreateObject(unsigned int number, const char *label) noexcep
}
// This is called when we have found an object label in a comment
-void ObjectTracker::StartObject(GCodeBuffer& gb, const char *label) noexcept
+size_t ObjectTracker::GetObjectNumber(const char *_ecv_array label) noexcept
{
- if (!usingM486Naming)
+ for (size_t i = 0; i < min<size_t>(numObjects, MaxTrackedObjects); ++i)
{
- for (size_t i = 0; i < min<size_t>(numObjects, MaxTrackedObjects); ++i)
+ if (strcmp(objectDirectory[i].name.Get().Ptr(), label) == 0)
{
- if (strcmp(objectDirectory[i].name.Get().Ptr(), label) == 0)
- {
- ChangeToObject(gb, i);
- return;
- }
- }
-
- // The object was not found, so add it
- if (numObjects < MaxTrackedObjects)
- {
- const int newObjectNumber = numObjects;
- CreateObject(newObjectNumber, label);
- ChangeToObject(gb, newObjectNumber);
- }
- else
- {
- // Here if the new object won't fit in the directory
- ChangeToObject(gb, MaxTrackedObjects);
+ return i;
}
}
-}
-// This is called when we have found a "stop printing object" comment
-void ObjectTracker::StopObject(GCodeBuffer& gb) noexcept
-{
- if (!usingM486Naming)
+ // The object was not found, so add it
+ if (numObjects < MaxTrackedObjects)
+ {
+ const int newObjectNumber = numObjects;
+ CreateObject(newObjectNumber, label);
+ return newObjectNumber;
+ }
+ else
{
- ChangeToObject(gb, -1);
+ // Here if the new object won't fit in the directory
+ return MaxTrackedObjects;
}
}
ExpressionValue ObjectTracker::GetXCoordinate(const ObjectExplorationContext& context) const noexcept
{
- const int16_t val = objectDirectory[context.GetIndex(1)].x[context.GetIndex(0)];
+ const int16_t val = objectDirectory[context.GetIndex(1)].x[context.GetLastIndex()];
return (val == std::numeric_limits<int16_t>::min()) ? ExpressionValue(nullptr) : ExpressionValue((int32_t)val);
}
ExpressionValue ObjectTracker::GetYCoordinate(const ObjectExplorationContext& context) const noexcept
{
- const int16_t val = objectDirectory[context.GetIndex(1)].y[context.GetIndex(0)];
+ const int16_t val = objectDirectory[context.GetIndex(1)].y[context.GetLastIndex()];
return (val == std::numeric_limits<int16_t>::min()) ? ExpressionValue(nullptr) : ExpressionValue((int32_t)val);
}
-#endif
-
// End
diff --git a/src/GCodes/ObjectTracker.h b/src/GCodes/ObjectTracker.h
index 6f5d8670..1f5113ef 100644
--- a/src/GCodes/ObjectTracker.h
+++ b/src/GCodes/ObjectTracker.h
@@ -13,8 +13,6 @@
#include <ObjectModel/ObjectModel.h>
#include <Platform/Heap.h>
-#if TRACK_OBJECT_NAMES
-
struct ObjectDirectoryEntry
{
AutoStringHandle name; // pointer to the object name within the string buffer
@@ -25,12 +23,8 @@ struct ObjectDirectoryEntry
void SetName(const char *label) noexcept;
};
-#endif
-
class ObjectTracker
-#if TRACK_OBJECT_NAMES
INHERIT_OBJECT_MODEL
-#endif
{
public:
ObjectTracker() noexcept
@@ -38,32 +32,27 @@ public:
{ }
void Init() noexcept;
- bool IsCurrentObjectCancelled() const noexcept { return currentObjectCancelled; }
- bool IsFirstMoveSincePrintingResumed() const noexcept { return printingJustResumed; }
- void DoneMoveSincePrintingResumed() noexcept { printingJustResumed = false; }
- GCodeResult HandleM486(GCodeBuffer& gb, const StringRef &reply, OutputBuffer*& buf) THROWS(GCodeException); // Handle M486
- const RestorePoint& GetInitialPosition() const noexcept { return rp; }
- void SetVirtualTool(int toolNum) noexcept { virtualToolNumber = toolNum; }
-#if TRACK_OBJECT_NAMES
- void StartObject(GCodeBuffer& gb, const char *label) noexcept;
- void StopObject(GCodeBuffer& gb) noexcept;
- void UpdateObjectCoordinates(const float coords[], AxesBitmap axes) noexcept;
+ // Functions to implement aspects of M486
+ void HandleM486T(unsigned int p_numObjects) noexcept;
+ void UseM486Labelling() noexcept;
+ void SetM486Label(unsigned int objectNumber, const char *_ecv_array label) noexcept;
+ bool CancelObject(unsigned int objectNumber) noexcept; // cancel an object, returning true if it was not already cancelled
+ bool ResumeObject(unsigned int objectNumber) noexcept; // resume an object, returning true if it was not already enabled
+ void ListObjects(OutputBuffer *buf) noexcept; // list the objects on the build plate
+
+ size_t GetObjectNumber(const char *label) noexcept;
+ void UpdateObjectCoordinates(int objectNumber, const float coords[], AxesBitmap axes) noexcept;
bool IsCancelled(size_t objectNumber) const noexcept { return objectsCancelled.IsBitSet(objectNumber); }
-#endif
+ bool IsUsingM486Naming() const noexcept { return usingM486Naming; }
+ bool CheckObject(int objectNumber) noexcept; // Add the object number if it isn't already known, return true if it has been cancelled
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
bool WriteObjectDirectory(FileStore *f) const noexcept;
#endif
protected:
-
-#if TRACK_OBJECT_NAMES
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(objects)
- OBJECT_MODEL_ARRAY(x)
- OBJECT_MODEL_ARRAY(y)
-#endif
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
#if SAME70 || SAME5x
@@ -73,30 +62,16 @@ private:
#endif
static_assert(MaxTrackedObjects <= ObjectCancellationBitmap::MaxBits());
- void ChangeToObject(GCodeBuffer& gb, int i) noexcept;
- void StopPrinting(GCodeBuffer& gb) noexcept;
- void ResumePrinting(GCodeBuffer& gb) noexcept;
-
-#if TRACK_OBJECT_NAMES
void CreateObject(unsigned int number, const char *label) noexcept;
ExpressionValue GetXCoordinate(const ObjectExplorationContext& context) const noexcept;
ExpressionValue GetYCoordinate(const ObjectExplorationContext& context) const noexcept;
-#endif
- RestorePoint rp; // The user coordinates at the point of restarting moves after skipping an object
ObjectCancellationBitmap objectsCancelled; // Which object numbers have been cancelled. The number of bits in this is the maximum number of objects we can track.
unsigned int numObjects; // How many objects we know about, if known
- int currentObjectNumber; // the current object number, or a negative value if it isn't an object
- int virtualToolNumber; // the number of the tool that was active when we cancelled an object
-#if TRACK_OBJECT_NAMES
ObjectDirectoryEntry objectDirectory[MaxTrackedObjects];
bool usingM486Naming;
-#endif
-
bool usingM486Labelling;
- bool currentObjectCancelled; // true if the current object should not be printed
- bool printingJustResumed; // true if we have just restarted printing
};
#endif /* SRC_GCODES_OBJECTTRACKER_H_ */
diff --git a/src/GCodes/RestorePoint.cpp b/src/GCodes/RestorePoint.cpp
index f197b25d..1fbdc508 100644
--- a/src/GCodes/RestorePoint.cpp
+++ b/src/GCodes/RestorePoint.cpp
@@ -19,19 +19,24 @@
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(RestorePoint, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(_condition,...) OBJECT_MODEL_FUNC_IF_BODY(RestorePoint, _condition,__VA_ARGS__)
-constexpr ObjectModelArrayDescriptor RestorePoint::coordinatesArrayDescriptor =
+constexpr ObjectModelArrayTableEntry RestorePoint::objectModelArrayTable[] =
{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const RestorePoint*)self)->moveCoords[context.GetLastIndex()], 3); }
+ // 0. Coordinates
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const RestorePoint*)self)->moveCoords[context.GetLastIndex()], 3); }
+ }
};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(RestorePoint)
+
constexpr ObjectModelTableEntry RestorePoint::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. LaserFilamentMonitor members
- { "coords", OBJECT_MODEL_FUNC_NOSELF(&coordinatesArrayDescriptor), ObjectModelEntryFlags::none },
+ { "coords", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
{ "extruderPos", OBJECT_MODEL_FUNC(self->virtualExtruderPosition, 1), ObjectModelEntryFlags::none },
{ "fanPwm", OBJECT_MODEL_FUNC(self->fanSpeed, 2), ObjectModelEntryFlags::none },
{ "feedRate", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerSec(self->feedRate), 1), ObjectModelEntryFlags::none },
diff --git a/src/GCodes/RestorePoint.h b/src/GCodes/RestorePoint.h
index 5b581691..cc3b007d 100644
--- a/src/GCodes/RestorePoint.h
+++ b/src/GCodes/RestorePoint.h
@@ -31,8 +31,7 @@ public:
void Init() noexcept;
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(coordinates)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
};
#endif /* SRC_GCODES_RESTOREPOINT_H_ */
diff --git a/src/Hardware/ExceptionHandlers.cpp b/src/Hardware/ExceptionHandlers.cpp
index f2ecc122..7653a275 100644
--- a/src/Hardware/ExceptionHandlers.cpp
+++ b/src/Hardware/ExceptionHandlers.cpp
@@ -95,7 +95,7 @@
// Exception handlers
// By default the Usage Fault, Bus Fault and Memory Management fault handlers are not enabled,
// so they escalate to a Hard Fault and we don't need to provide separate exception handlers for them.
-extern "C" [[noreturn]] void hardFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+extern "C" [[noreturn]] __attribute__((externally_visible)) void hardFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
{
SoftwareReset(SoftwareResetReason::hardFault, pulFaultStackAddress);
}
@@ -143,7 +143,7 @@ void MemManage_Handler() noexcept
#endif
-extern "C" [[noreturn]] void wdtFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+extern "C" [[noreturn]] __attribute__((externally_visible)) void wdtFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
{
SoftwareReset(SoftwareResetReason::wdtFault, pulFaultStackAddress);
}
@@ -176,7 +176,7 @@ void WDT_Handler() noexcept
);
}
-extern "C" [[noreturn]] void otherFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+extern "C" [[noreturn]] __attribute__((externally_visible)) void otherFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
{
SoftwareReset(SoftwareResetReason::otherFault, pulFaultStackAddress);
}
@@ -210,7 +210,7 @@ extern "C" void UsageFault_Handler () noexcept { SoftwareReset(SoftwareResetReas
extern "C" void DebugMon_Handler () noexcept __attribute__ ((alias("OtherFault_Handler")));
// FreeRTOS hooks that we need to provide
-extern "C" [[noreturn]] void stackOverflowDispatcher(const uint32_t *pulFaultStackAddress, char* pcTaskName) noexcept
+extern "C" [[noreturn]] __attribute__((externally_visible)) void stackOverflowDispatcher(const uint32_t *pulFaultStackAddress, char* pcTaskName) noexcept
{
SoftwareReset(SoftwareResetReason::stackOverflow, pulFaultStackAddress);
}
@@ -230,7 +230,7 @@ void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexce
);
}
-extern "C" [[noreturn]] void assertCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+extern "C" [[noreturn]] __attribute__((externally_visible)) void assertCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
{
SoftwareReset(SoftwareResetReason::assertCalled, pulFaultStackAddress);
}
diff --git a/src/Hardware/SAM4E/sam4e8e_flash.ld b/src/Hardware/SAM4E/sam4e8e_flash.ld
index e7fc0113..fa10bf24 100644
--- a/src/Hardware/SAM4E/sam4e8e_flash.ld
+++ b/src/Hardware/SAM4E/sam4e8e_flash.ld
@@ -120,6 +120,9 @@ SECTIONS
_firmware_end = _etext + (_erelocate - _srelocate); /* Embedded files start here */
_firmware_crc = _firmware_end; /* We append the CRC32 to the binary file. This is its offset in memory if we don't append embedded files */
+ /* check that CRC is within flash memory */
+ ASSERT(_firmware_crc + 4 <= ORIGIN(rom) + LENGTH(rom), "region ROM overflowed")
+
/* .bss section which is used for uninitialized data */
.bss ALIGN(4) (NOLOAD) :
{
diff --git a/src/Hardware/SAME5x/Devices.h b/src/Hardware/SAME5x/Devices.h
index fa0a556c..3b36e28c 100644
--- a/src/Hardware/SAME5x/Devices.h
+++ b/src/Hardware/SAME5x/Devices.h
@@ -14,7 +14,7 @@
extern AsyncSerial serialUart0, serialUart1;
#define SUPPORT_USB 1 // needed by SerialCDC.h
-#include "SerialCDC.h"
+#include <SerialCDC.h>
extern SerialCDC serialUSB;
diff --git a/src/Hardware/SharedSpi/SharedSpiDevice.h b/src/Hardware/SharedSpi/SharedSpiDevice.h
deleted file mode 100644
index 4bb99496..00000000
--- a/src/Hardware/SharedSpi/SharedSpiDevice.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SharedSpiDevice.h
- *
- * Created on: 16 Jun 2020
- * Author: David
- */
-
-#ifndef SRC_HARDWARE_SHAREDSPI_SHAREDSPIDEVICE_H_
-#define SRC_HARDWARE_SHAREDSPI_SHAREDSPIDEVICE_H_
-
-#include <RepRapFirmware.h>
-#include <RTOSIface/RTOSIface.h>
-#include "SpiMode.h"
-
-class SharedSpiDevice
-{
-public:
- SharedSpiDevice(uint8_t sercomNum) noexcept;
-
- void Disable() const noexcept;
- void Enable() const noexcept;
- void SetClockFrequencyAndMode(uint32_t freq, SpiMode mode) const noexcept;
-
- // Send and receive data returning true if successful. Caller must already own the mutex and have asserted CS.
- bool TransceivePacket(const uint8_t *tx_data, uint8_t *rx_data, size_t len) const noexcept;
-
- // Get ownership of this SPI, return true if successful
- bool Take(uint32_t timeout) noexcept { return mutex.Take(timeout); }
-
- // Release ownership of this SPI
- void Release() noexcept { mutex.Release(); }
-
- static void Init() noexcept;
- static SharedSpiDevice& GetMainSharedSpiDevice() noexcept { return *mainSharedSpiDevice; }
-
-private:
- bool waitForTxReady() const noexcept;
- bool waitForTxEmpty() const noexcept;
- bool waitForRxReady() const noexcept;
-
-#if SAME5x
- Sercom * const hardware;
-#elif USART_SPI
- Usart * const hardware;
-#else
- Spi * const hardware;
-#endif
-
- Mutex mutex;
-
- static SharedSpiDevice *mainSharedSpiDevice;
-};
-
-#endif /* SRC_HARDWARE_SHAREDSPI_SHAREDSPIDEVICE_H_ */
diff --git a/src/Hardware/SharedSpi/SharedSpiClient.cpp b/src/Hardware/Spi/SharedSpiClient.cpp
index 033d46bd..885aa176 100644
--- a/src/Hardware/SharedSpi/SharedSpiClient.cpp
+++ b/src/Hardware/Spi/SharedSpiClient.cpp
@@ -30,7 +30,11 @@ bool SharedSpiClient::Select(uint32_t timeout) const noexcept
const bool ok = device.Take(timeout);
if (ok)
{
- device.SetClockFrequencyAndMode(clockFrequency, mode); // this also enables the SPI peripheral
+ device.SetClockFrequencyAndMode(clockFrequency, mode
+#if SAME5x
+ , false // for now we always use 8-bit mode
+#endif
+ ); // this also enables the SPI peripheral
delayMicroseconds(1); // allow the clock time to settle
IoPort::WriteDigital(csPin, csActivePolarity);
}
diff --git a/src/Hardware/SharedSpi/SharedSpiClient.h b/src/Hardware/Spi/SharedSpiClient.h
index 19f56e56..75b43a8d 100644
--- a/src/Hardware/SharedSpi/SharedSpiClient.h
+++ b/src/Hardware/Spi/SharedSpiClient.h
@@ -8,8 +8,8 @@
* configured in SPI mode a separate object, and have a pointer or reference to it in SharedSpiDevice.
*/
-#ifndef SRC_HARDWARE_SHAREDSPI_SHAREDSPICLIENT_H_
-#define SRC_HARDWARE_SHAREDSPI_SHAREDSPICLIENT_H_
+#ifndef SRC_HARDWARE_SPI_SHAREDSPICLIENT_H_
+#define SRC_HARDWARE_SPI_SHAREDSPICLIENT_H_
#include <RepRapFirmware.h>
#include "SpiMode.h"
@@ -44,4 +44,4 @@ private:
bool csActivePolarity;
};
-#endif /* SRC_HARDWARE_SHAREDSPI_SHAREDSPICLIENT_H_ */
+#endif /* SRC_HARDWARE_SPI_SHAREDSPICLIENT_H_ */
diff --git a/src/Hardware/Spi/SharedSpiDevice.cpp b/src/Hardware/Spi/SharedSpiDevice.cpp
new file mode 100644
index 00000000..52c343bd
--- /dev/null
+++ b/src/Hardware/Spi/SharedSpiDevice.cpp
@@ -0,0 +1,47 @@
+/*
+ * SharedSpiDevice.cpp
+ *
+ * Created on: 16 Jun 2020
+ * Author: David
+ */
+
+#include "SharedSpiDevice.h"
+
+// SharedSpiDevice members
+
+SharedSpiDevice::SharedSpiDevice(uint8_t sercomNum) noexcept
+ : SpiDevice(sercomNum)
+{
+ mutex.Create("SPI");
+}
+
+// Static members
+
+SharedSpiDevice *SharedSpiDevice::mainSharedSpiDevice = nullptr;
+
+void SharedSpiDevice::Init() noexcept
+{
+#if SAME5x
+ pinMode(SharedSpiMosiPin, INPUT_PULLDOWN);
+ pinMode(SharedSpiMisoPin, INPUT_PULLDOWN);
+ pinMode(SharedSpiSclkPin, INPUT_PULLDOWN);
+ SetPinFunction(SharedSpiMosiPin, SharedSpiPinFunction);
+ SetPinFunction(SharedSpiMisoPin, SharedSpiPinFunction);
+ SetPinFunction(SharedSpiSclkPin, SharedSpiPinFunction);
+ SetDriveStrength(SharedSpiMosiPin, 2);
+ SetDriveStrength(SharedSpiSclkPin, 2); // some devices (e.g. TFT LCD font chip) need fast rise and fall times
+ mainSharedSpiDevice = new SharedSpiDevice(SharedSpiSercomNumber);
+#elif USART_SPI
+ SetPinFunction(APIN_USART_SSPI_SCK, USARTSPISckPeriphMode);
+ SetPinFunction(APIN_USART_SSPI_MOSI, USARTSPIMosiPeriphMode);
+ SetPinFunction(APIN_USART_SSPI_MISO, USARTSPIMisoPeriphMode);
+ mainSharedSpiDevice = new SharedSpiDevice(0);
+#else
+ ConfigurePin(g_APinDescription[APIN_SHARED_SPI_SCK]);
+ ConfigurePin(g_APinDescription[APIN_SHARED_SPI_MOSI]);
+ ConfigurePin(g_APinDescription[APIN_SHARED_SPI_MISO]);
+ mainSharedSpiDevice = new SharedSpiDevice(0);
+#endif
+}
+
+// End
diff --git a/src/Hardware/Spi/SharedSpiDevice.h b/src/Hardware/Spi/SharedSpiDevice.h
new file mode 100644
index 00000000..37242b8c
--- /dev/null
+++ b/src/Hardware/Spi/SharedSpiDevice.h
@@ -0,0 +1,33 @@
+/*
+ * SharedSpiDevice.h
+ *
+ * Created on: 16 Jun 2020
+ * Author: David
+ */
+
+#ifndef SRC_HARDWARE_SPI_SHAREDSPIDEVICE_H_
+#define SRC_HARDWARE_SPI_SHAREDSPIDEVICE_H_
+
+#include "SpiDevice.h"
+
+class SharedSpiDevice : public SpiDevice
+{
+public:
+ SharedSpiDevice(uint8_t sercomNum) noexcept;
+
+ // Get ownership of this SPI, return true if successful
+ bool Take(uint32_t timeout) noexcept { return mutex.Take(timeout); }
+
+ // Release ownership of this SPI
+ void Release() noexcept { mutex.Release(); }
+
+ static void Init() noexcept;
+ static SharedSpiDevice& GetMainSharedSpiDevice() noexcept { return *mainSharedSpiDevice; }
+
+private:
+ Mutex mutex;
+
+ static SharedSpiDevice *mainSharedSpiDevice;
+};
+
+#endif /* SRC_HARDWARE_SPI_SHAREDSPIDEVICE_H_ */
diff --git a/src/Hardware/SharedSpi/SharedSpiDevice.cpp b/src/Hardware/Spi/SpiDevice.cpp
index 6b64b058..d8f1a036 100644
--- a/src/Hardware/SharedSpi/SharedSpiDevice.cpp
+++ b/src/Hardware/Spi/SpiDevice.cpp
@@ -1,16 +1,14 @@
/*
- * SharedSpiDevice.cpp
+ * SpiDevice.cpp
*
- * Created on: 16 Jun 2020
+ * Created on: 7 May 2022
* Author: David
*/
-#include "SharedSpiDevice.h"
-
+#include "SpiDevice.h"
#include <Hardware/IoPorts.h>
#if SAME5x
-# include <DmacManager.h>
# include <Serial.h>
# include <peripheral_clk_config.h>
#elif USART_SPI
@@ -25,11 +23,9 @@
constexpr uint32_t DefaultSharedSpiClockFrequency = 2000000;
constexpr uint32_t SpiTimeout = 10000;
-// SharedSpiDevice members
-
-SharedSpiDevice::SharedSpiDevice(uint8_t sercomNum) noexcept
+SpiDevice::SpiDevice(uint8_t sercomNum) noexcept
#if SAME5x
- : hardware(Serial::Sercoms[sercomNum])
+ : hardware(Serial::Sercoms[sercomNum]), sercomNumber(sercomNum)
#elif USART_SPI
: hardware(USART_SSPI) // we ignore the parameter and support just one shared SPI
#else
@@ -37,7 +33,6 @@ SharedSpiDevice::SharedSpiDevice(uint8_t sercomNum) noexcept
#endif
{
#if SAME5x
-
Serial::EnableSercomClock(sercomNum);
// Set up the SERCOM
@@ -45,23 +40,23 @@ SharedSpiDevice::SharedSpiDevice(uint8_t sercomNum) noexcept
const uint32_t regCtrlB = 0; // 8 bits, slave select disabled, receiver disabled for now
const uint32_t regCtrlC = 0; // not 32-bit mode
- if ((hardware->USART.SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_SWRST) == 0)
+ if ((hardware->SPI.SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_SWRST) == 0)
{
- while (hardware->USART.SYNCBUSY.reg & (SERCOM_USART_SYNCBUSY_SWRST | SERCOM_USART_SYNCBUSY_ENABLE)) { }
- if (hardware->USART.CTRLA.reg & SERCOM_USART_CTRLA_ENABLE)
+ while (hardware->SPI.SYNCBUSY.reg & (SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_ENABLE)) { }
+ if (hardware->SPI.CTRLA.reg & SERCOM_SPI_CTRLA_ENABLE)
{
- hardware->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;
- while (hardware->USART.SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_ENABLE) { }
+ hardware->SPI.CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE;
+ while (hardware->SPI.SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) { }
}
- hardware->USART.CTRLA.reg = SERCOM_USART_CTRLA_SWRST | (regCtrlA & SERCOM_USART_CTRLA_MODE_Msk);
+ hardware->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_SWRST | (regCtrlA & SERCOM_SPI_CTRLA_MODE_Msk);
}
while (hardware->USART.SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_SWRST) { }
- hardware->USART.CTRLA.reg = regCtrlA;
- hardware->USART.CTRLB.reg = regCtrlB;
- hardware->USART.CTRLC.reg = regCtrlC;
- hardware->USART.BAUD.reg = SERCOM_SPI_BAUD_BAUD(Serial::SercomFastGclkFreq/(2 * DefaultSharedSpiClockFrequency) - 1);
- hardware->USART.DBGCTRL.reg = SERCOM_I2CM_DBGCTRL_DBGSTOP; // baud rate generator is stopped when CPU halted by debugger
+ hardware->SPI.CTRLA.reg = regCtrlA;
+ hardware->SPI.CTRLB.reg = regCtrlB;
+ hardware->SPI.CTRLC.reg = regCtrlC;
+ hardware->SPI.BAUD.reg = SERCOM_SPI_BAUD_BAUD(Serial::SercomFastGclkFreq/(2 * DefaultSharedSpiClockFrequency) - 1);
+ hardware->SPI.DBGCTRL.reg = SERCOM_SPI_DBGCTRL_DBGSTOP; // baud rate generator is stopped when CPU halted by debugger
#if 0 // if using DMA
// Set up the DMA descriptors
@@ -106,17 +101,13 @@ SharedSpiDevice::SharedSpiDevice(uint8_t sercomNum) noexcept
hardware->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS;
#endif
-
- mutex.Create("SPI");
}
-// SharedSpiClient members
-
-void SharedSpiDevice::Disable() const noexcept
+void SpiDevice::Disable() const noexcept
{
#if SAME5x
hardware->SPI.CTRLA.bit.ENABLE = 0;
- while (hardware->USART.SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_ENABLE) { }
+ while (hardware->SPI.SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) { }
#elif USART_SPI
hardware->US_CR = US_CR_RXDIS | US_CR_TXDIS; // disable transmitter and receiver
#else
@@ -124,11 +115,11 @@ void SharedSpiDevice::Disable() const noexcept
#endif
}
-void SharedSpiDevice::Enable() const noexcept
+void SpiDevice::Enable() const noexcept
{
#if SAME5x
hardware->SPI.CTRLA.bit.ENABLE = 1;
- while (hardware->USART.SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_ENABLE) { }
+ while (hardware->SPI.SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) { }
#elif USART_SPI
hardware->US_CR = US_CR_RXEN | US_CR_TXEN; // enable transmitter and receiver
#else
@@ -137,7 +128,7 @@ void SharedSpiDevice::Enable() const noexcept
}
// Wait for transmitter ready returning true if timed out
-inline bool SharedSpiDevice::waitForTxReady() const noexcept
+inline bool SpiDevice::waitForTxReady() const noexcept
{
uint32_t timeout = SpiTimeout;
#if SAME5x
@@ -157,7 +148,7 @@ inline bool SharedSpiDevice::waitForTxReady() const noexcept
}
// Wait for transmitter empty returning true if timed out
-inline bool SharedSpiDevice::waitForTxEmpty() const noexcept
+inline bool SpiDevice::waitForTxEmpty() const noexcept
{
uint32_t timeout = SpiTimeout;
#if SAME5x
@@ -177,7 +168,7 @@ inline bool SharedSpiDevice::waitForTxEmpty() const noexcept
}
// Wait for receive data available returning true if timed out
-inline bool SharedSpiDevice::waitForRxReady() const noexcept
+inline bool SpiDevice::waitForRxReady() const noexcept
{
uint32_t timeout = SpiTimeout;
#if SAME5x
@@ -196,16 +187,22 @@ inline bool SharedSpiDevice::waitForRxReady() const noexcept
return false;
}
-void SharedSpiDevice::SetClockFrequencyAndMode(uint32_t freq, SpiMode mode) const noexcept
+void SpiDevice::SetClockFrequencyAndMode(uint32_t freq, SpiMode mode
+#if SAME5x
+ , bool nineBits
+#endif
+ ) const noexcept
{
- // We have to disable SPI device in order to change the baud rate and mode
+ // We have to disable SPI device in order to change the baud rate, mode and character length
#if SAME5x
Disable();
// Round the clock frequency rate down. For example, using 60MHz clock, if we ask for 4MHz:
// Without rounding, divisor = 60/(2*4) = 7, actual clock rate = 4.3MHz
// With rounding, divisor = 67/8 = 8, actual clock rate = 3.75MHz
// To get more accurate speeds we could increase the clock frequency to 100MHz
- hardware->USART.BAUD.reg = SERCOM_SPI_BAUD_BAUD((Serial::SercomFastGclkFreq + (2 * freq) - 1)/(2 * freq) - 1);
+ hardware->SPI.BAUD.reg = SERCOM_SPI_BAUD_BAUD((Serial::SercomFastGclkFreq + (2 * freq) - 1)/(2 * freq) - 1);
+ hardware->SPI.CTRLB.bit.CHSIZE = (nineBits) ? 1 : 0;
+ while (hardware->SPI.SYNCBUSY.bit.CTRLB) { }
uint32_t regCtrlA = SERCOM_SPI_CTRLA_MODE(3) | SERCOM_SPI_CTRLA_DIPO(3) | SERCOM_SPI_CTRLA_DOPO(0) | SERCOM_SPI_CTRLA_FORM(0);
if (((uint8_t)mode & 2) != 0)
@@ -216,7 +213,7 @@ void SharedSpiDevice::SetClockFrequencyAndMode(uint32_t freq, SpiMode mode) cons
{
regCtrlA |= SERCOM_SPI_CTRLA_CPHA;
}
- hardware->USART.CTRLA.reg = regCtrlA;
+ hardware->SPI.CTRLA.reg = regCtrlA;
Enable();
#elif USART_SPI
Disable();
@@ -260,7 +257,7 @@ void SharedSpiDevice::SetClockFrequencyAndMode(uint32_t freq, SpiMode mode) cons
}
// Send and receive data returning true if successful
-bool SharedSpiDevice::TransceivePacket(const uint8_t* tx_data, uint8_t* rx_data, size_t len) const noexcept
+bool SpiDevice::TransceivePacket(const uint8_t *_ecv_array null tx_data, uint8_t *_ecv_array null rx_data, size_t len) const noexcept
{
// Clear any existing data
#if SAME5x
@@ -336,33 +333,118 @@ bool SharedSpiDevice::TransceivePacket(const uint8_t* tx_data, uint8_t* rx_data,
return true; // success
}
-// Static members
+#if SAME5x && (defined(FMDC_V02) || defined(FMDC_V03))
-SharedSpiDevice *SharedSpiDevice::mainSharedSpiDevice = nullptr;
-
-void SharedSpiDevice::Init() noexcept
+// Send and receive data returning true if successful, using 16-bit data transfers (needed when using 9-bit characters). 'len' is in 16-bit words.
+bool SpiDevice::TransceivePacketNineBit(const uint16_t *_ecv_array null tx_data, uint16_t *_ecv_array null rx_data, size_t len) noexcept
{
+ // Clear any existing data
+#if SAME5x
+ (void)hardware->SPI.DATA.reg;
+#elif USART_SPI
+ (void)hardware->US_RHR;
+#else
+ (void)hardware->SPI_RDR;
+#endif
+
+#if SAME5x
+ if (len >= 50 && rx_data == nullptr && tx_data != nullptr)
+ {
+ // Sending a large amount of data to LCD, so use DMA. Currently only the TFT LCD uses this device, so we use a fixed DMA channel number.
+ DmacManager::DisableChannel(DmacChanLcdTx);
+ DmacManager::SetSourceAddress(DmacChanLcdTx, tx_data);
+ DmacManager::SetDestinationAddress(DmacChanLcdTx, &(hardware->SPI.DATA));
+ DmacManager::SetBtctrl(DmacChanLcdTx, DMAC_BTCTRL_STEPSIZE_X1 | DMAC_BTCTRL_STEPSEL_SRC | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_BLOCKACT_NOACT);
+ DmacManager::SetDataLength(DmacChanLcdTx, len);
+ DmacManager::SetTriggerSourceSercomTx(DmacChanLcdTx, sercomNumber);
+ waitingTask = TaskBase::GetCallerTaskHandle();
+ DmacManager::SetInterruptCallback(DmacChanLcdTx, SpiDevice::DmaComplete, CallbackParameter((void *)this));
+ DmacManager::EnableCompletedInterrupt(DmacChanLcdTx);
+ DmacManager::EnableChannel(DmacChanLcdTx, DmacPrioLcdTx);
+ TaskBase::Take(10); // maximum 3kb transfer should complete in about 2ms @ 14MHz clock speed
+ }
+ else
+#endif
+ {
+ for (uint32_t i = 0; i < len; ++i)
+ {
+ uint32_t dOut = (tx_data == nullptr) ? 0x000001FF : (uint32_t)*tx_data++;
+ if (waitForTxReady()) // we have to write the first byte after enabling the device without waiting for DRE to be set
+ {
+ return false;
+ }
+
+ // Write to transmit register
+#if SAME5x
+ hardware->SPI.DATA.reg = dOut;
+#elif USART_SPI
+ hardware->US_THR = dOut;
+#else
+ if (i + 1 == len)
+ {
+ dOut |= SPI_TDR_LASTXFER;
+ }
+ hardware->SPI_TDR = dOut;
+#endif
+
+ // Some devices are transmit-only e.g. 12864 display, so don't wait for received data if we don't need to
+ if (rx_data != nullptr)
+ {
+ // Wait for receive register
+ if (waitForRxReady())
+ {
+ return false;
+ }
+
+ // Get data from receive register
+ const uint16_t dIn =
#if SAME5x
- pinMode(SharedSpiMosiPin, INPUT_PULLDOWN);
- pinMode(SharedSpiMisoPin, INPUT_PULLDOWN);
- pinMode(SharedSpiSclkPin, INPUT_PULLDOWN);
- SetPinFunction(SharedSpiMosiPin, SharedSpiPinFunction);
- SetPinFunction(SharedSpiMisoPin, SharedSpiPinFunction);
- SetPinFunction(SharedSpiSclkPin, SharedSpiPinFunction);
- SetHighDriveStrength(SharedSpiMosiPin);
- SetHighDriveStrength(SharedSpiSclkPin); // some devices (e.g. TFT LCD font chip) need fast rise and fall times
- mainSharedSpiDevice = new SharedSpiDevice(SharedSpiSercomNumber);
+ (uint16_t)hardware->SPI.DATA.reg;
#elif USART_SPI
- SetPinFunction(APIN_USART_SSPI_SCK, USARTSPISckPeriphMode);
- SetPinFunction(APIN_USART_SSPI_MOSI, USARTSPIMosiPeriphMode);
- SetPinFunction(APIN_USART_SSPI_MISO, USARTSPIMisoPeriphMode);
- mainSharedSpiDevice = new SharedSpiDevice(0);
+ (uint16_t)hardware->US_RHR;
#else
- ConfigurePin(g_APinDescription[APIN_SHARED_SPI_SCK]);
- ConfigurePin(g_APinDescription[APIN_SHARED_SPI_MOSI]);
- ConfigurePin(g_APinDescription[APIN_SHARED_SPI_MISO]);
- mainSharedSpiDevice = new SharedSpiDevice(0);
+ (uint16_t)hardware->SPI_RDR;
#endif
+ *rx_data++ = dIn;
+ }
+ }
+
+ }
+
+ // Wait for transmitter empty, to make sure that the last clock pulse has finished
+ waitForTxEmpty();
+
+ // If we were not receiving, clear data from the receive buffer
+ if (rx_data == nullptr)
+ {
+#if SAME5x
+ // The SAME5x seems to buffer more than one received character
+ while (hardware->SPI.INTFLAG.bit.RXC)
+ {
+ (void)hardware->SPI.DATA.reg;
+ }
+#elif USART_SPI
+ (void)hardware->US_RHR;
+#else
+ (void)hardware->SPI_RDR;
+#endif
+ }
+
+ return true; // success
}
+void SpiDevice::DmaComplete(DmaCallbackReason reason) noexcept
+{
+ TaskBase::GiveFromISR(waitingTask);
+ waitingTask = nullptr;
+}
+
+/*static*/ void SpiDevice::DmaComplete(CallbackParameter param, DmaCallbackReason reason) noexcept
+{
+ static_cast<SpiDevice*>(param.vp)->DmaComplete(reason);
+}
+
+#endif
+
// End
+
diff --git a/src/Hardware/Spi/SpiDevice.h b/src/Hardware/Spi/SpiDevice.h
new file mode 100644
index 00000000..895c4bc3
--- /dev/null
+++ b/src/Hardware/Spi/SpiDevice.h
@@ -0,0 +1,68 @@
+/*
+ * SpiDevice.h
+ *
+ * Created on: 7 May 2022
+ * Author: David
+ */
+
+#ifndef SRC_HARDWARE_SPI_SPIDEVICE_H_
+#define SRC_HARDWARE_SPI_SPIDEVICE_H_
+
+#include <RepRapFirmware.h>
+#include "SpiMode.h"
+
+#if SAME5x && (defined(FMDC_V02) || defined(FMDC_V03))
+# include <DmacManager.h>
+#endif
+
+// This class represents a master SPI interface, but not the associated CS pin(s).
+// It is used as the base class for SharedSpiDevice. It can also be used by itself to control a non-shared SPI master.
+class SpiDevice
+{
+public:
+ SpiDevice(uint8_t sercomNum) noexcept;
+
+ void Disable() const noexcept;
+ void Enable() const noexcept;
+
+ // Set the clock frequency, SPI mode and character length. 9-bit mode is currently only implemented on the SAME5x.
+ void SetClockFrequencyAndMode(uint32_t freq, SpiMode mode
+#if SAME5x
+ , bool nineBits
+#endif
+ ) const noexcept;
+
+ // Send and receive data returning true if successful.
+ // If this is a shared SPI device then the caller must already own the mutex.
+ // Either way, caller must already have asserted CS for the selected SPI slave.
+ bool TransceivePacket(const uint8_t *_ecv_array null tx_data, uint8_t *_ecv_array null rx_data, size_t len) const noexcept;
+
+#if SAME5x && (defined(FMDC_V02) || defined(FMDC_V03))
+ bool TransceivePacketNineBit(const uint16_t *_ecv_array null tx_data, uint16_t *_ecv_array null rx_data, size_t len) noexcept;
+
+ static void DmaComplete(CallbackParameter param, DmaCallbackReason reason) noexcept;
+#endif
+
+private:
+ bool waitForTxReady() const noexcept;
+ bool waitForTxEmpty() const noexcept;
+ bool waitForRxReady() const noexcept;
+
+#if SAME5x && (defined(FMDC_V02) || defined(FMDC_V03))
+ void DmaComplete(DmaCallbackReason reason) noexcept;
+#endif
+
+#if SAME5x
+ Sercom * const hardware;
+ const uint8_t sercomNumber;
+# if defined(FMDC_V02) || defined(FMDC_V03)
+ TaskBase *null waitingTask = nullptr;
+# endif
+#elif USART_SPI
+ Usart * const hardware;
+#else
+ Spi * const hardware;
+#endif
+};
+
+#endif /* SRC_HARDWARE_SPI_SPIDEVICE_H_ */
diff --git a/src/Hardware/SharedSpi/SpiMode.h b/src/Hardware/Spi/SpiMode.h
index a30544f6..f7a4c468 100644
--- a/src/Hardware/SharedSpi/SpiMode.h
+++ b/src/Hardware/Spi/SpiMode.h
@@ -5,8 +5,8 @@
* Author: David
*/
-#ifndef SRC_HARDWARE_SHAREDSPI_SPIMODE_H_
-#define SRC_HARDWARE_SHAREDSPI_SPIMODE_H_
+#ifndef SRC_HARDWARE_SPI_SPIMODE_H_
+#define SRC_HARDWARE_SPI_SPIMODE_H_
enum class SpiMode : uint8_t
{
@@ -19,4 +19,4 @@ constexpr SpiMode SPI_MODE_1 = SpiMode::mode1;
constexpr SpiMode SPI_MODE_2 = SpiMode::mode2;
constexpr SpiMode SPI_MODE_3 = SpiMode::mode3;
-#endif /* SRC_HARDWARE_SHAREDSPI_SPIMODE_H_ */
+#endif /* SRC_HARDWARE_SPI_SPIMODE_H_ */
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index 932ee8e4..37a131f8 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -65,43 +65,47 @@ extern "C" [[noreturn]] void HeaterTaskStart(void * pvParameters) noexcept
}
#if SUPPORT_OBJECT_MODEL
+
+// Macro to build a standard lambda function that includes the necessary type conversions
+#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Heat, __VA_ARGS__)
+
// Object model table and functions
// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17.
// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM.
-constexpr ObjectModelArrayDescriptor Heat::bedHeatersArrayDescriptor =
-{
- &heatersLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MaxBedHeaters; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Heat*)self)->bedHeaters[context.GetLastIndex()]); }
-};
-
-constexpr ObjectModelArrayDescriptor Heat::chamberHeatersArrayDescriptor =
-{
- &heatersLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MaxChamberHeaters; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Heat*)self)->chamberHeaters[context.GetLastIndex()]); }
-};
-
-constexpr ObjectModelArrayDescriptor Heat::heatersArrayDescriptor =
+constexpr ObjectModelArrayTableEntry Heat::objectModelArrayTable[] =
{
- &heatersLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Heat*)self)->GetNumHeatersToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->heaters[context.GetLastIndex()]); }
+ // 0. Bed heaters
+ {
+ &heatersLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MaxBedHeaters; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Heat*)self)->bedHeaters[context.GetLastIndex()]); }
+ },
+ // 1. Chamber heaters
+ {
+ &heatersLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MaxChamberHeaters; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Heat*)self)->chamberHeaters[context.GetLastIndex()]); }
+ },
+ // 2. Heaters
+ {
+ &heatersLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Heat*)self)->GetNumHeatersToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->heaters[context.GetLastIndex()]); }
+ }
};
-// Macro to build a standard lambda function that includes the necessary type conversions
-#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Heat, __VA_ARGS__)
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(Heat)
constexpr ObjectModelTableEntry Heat::objectModelTable[] =
{
// These entries must be in alphabetical order
// 0. Heat class
- { "bedHeaters", OBJECT_MODEL_FUNC_NOSELF(&bedHeatersArrayDescriptor), ObjectModelEntryFlags::none },
- { "chamberHeaters", OBJECT_MODEL_FUNC_NOSELF(&chamberHeatersArrayDescriptor), ObjectModelEntryFlags::none },
+ { "bedHeaters", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
+ { "chamberHeaters", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
{ "coldExtrudeTemperature", OBJECT_MODEL_FUNC((self->coldExtrude) ? 0.0f : self->extrusionMinTemp, 1), ObjectModelEntryFlags::none },
{ "coldRetractTemperature", OBJECT_MODEL_FUNC((self->coldExtrude) ? 0.0f : self->retractionMinTemp, 1), ObjectModelEntryFlags::none },
- { "heaters", OBJECT_MODEL_FUNC_NOSELF(&heatersArrayDescriptor), ObjectModelEntryFlags::live },
+ { "heaters", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::live },
};
constexpr uint8_t Heat::objectModelTableDescriptor[] = { 1, 5 };
@@ -143,8 +147,7 @@ Heat::Heat() noexcept
ReadLockedPointer<Heater> Heat::FindHeater(int heater) const noexcept
{
- ReadLocker locker(heatersLock);
- return ReadLockedPointer<Heater>(locker, (heater < 0 || heater >= (int)MaxHeaters) ? nullptr : heaters[heater]);
+ return ReadLockedPointer<Heater>(heatersLock, (heater < 0 || heater >= (int)MaxHeaters) ? nullptr : heaters[heater]);
}
// Process M307
@@ -928,18 +931,7 @@ GCodeResult Heat::ConfigureHeaterMonitoring(size_t heater, GCodeBuffer& gb, cons
return GCodeResult::error;
}
- bool seenValue = false;
- float maxTempExcursion, maxFaultTime;
- h->GetFaultDetectionParameters(maxTempExcursion, maxFaultTime);
- gb.TryGetFValue('P', maxFaultTime, seenValue);
- gb.TryGetFValue('T', maxTempExcursion, seenValue);
- if (seenValue)
- {
- return h->SetFaultDetectionParameters(maxTempExcursion, maxFaultTime, reply);
- }
-
- reply.printf("Heater %u allowed excursion %.1f" DEGREE_SYMBOL "C, fault trigger time %.1f seconds", heater, (double)maxTempExcursion, (double)maxFaultTime);
- return GCodeResult::ok;
+ return h->ConfigureFaultDetectionParameters(gb, reply);
}
// Process M303
@@ -957,7 +949,7 @@ GCodeResult Heat::TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS(GCo
if (seenTool)
{
const int toolNumber = gb.GetIValue();
- const auto tool = reprap.GetTool(toolNumber);
+ const auto tool = Tool::GetLockedTool(toolNumber);
if (tool.IsNull())
{
reply.printf("tool %d not found", toolNumber);
@@ -1447,7 +1439,7 @@ GCodeResult Heat::SetFaultDetection(const CanMessageSetHeaterFaultDetectionParam
{
const auto h = FindHeater(msg.heater);
return (h.IsNotNull())
- ? h->SetFaultDetectionParameters(msg.maxTempExcursion, msg.maxFaultTime, reply)
+ ? h->SetFaultDetectionParameters(msg, reply)
: UnknownHeater(msg.heater, reply);
}
diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h
index 6e7576b7..4854cc2a 100644
--- a/src/Heating/Heat.h
+++ b/src/Heating/Heat.h
@@ -155,10 +155,7 @@ public:
static ReadWriteLock sensorsLock; // needs to be public so that the OMT in EndstopsManager can lock it
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(bedHeaters)
- OBJECT_MODEL_ARRAY(chamberHeaters)
- OBJECT_MODEL_ARRAY(heaters)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
ReadLockedPointer<Heater> FindHeater(int heater) const noexcept;
diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp
index 74ecdcae..ef340c91 100644
--- a/src/Heating/Heater.cpp
+++ b/src/Heating/Heater.cpp
@@ -25,38 +25,45 @@
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Heater, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(...) OBJECT_MODEL_FUNC_IF_BODY(Heater, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor Heater::monitorsArrayDescriptor =
+constexpr ObjectModelArrayTableEntry Heater::objectModelArrayTable[] =
{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MaxMonitorsPerHeater; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 1); }
-
+ {
+ // 0. Monitors
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MaxMonitorsPerHeater; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 1); }
+ }
};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(Heater)
+
constexpr ObjectModelTableEntry Heater::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. Heater members
- { "active", OBJECT_MODEL_FUNC(self->GetActiveTemperature(), 1), ObjectModelEntryFlags::live },
- { "avgPwm", OBJECT_MODEL_FUNC(self->GetAveragePWM(), 3), ObjectModelEntryFlags::live },
- { "current", OBJECT_MODEL_FUNC(self->GetTemperature(), 2), ObjectModelEntryFlags::live },
- { "max", OBJECT_MODEL_FUNC(self->GetHighestTemperatureLimit(), 1), ObjectModelEntryFlags::none },
- { "min", OBJECT_MODEL_FUNC(self->GetLowestTemperatureLimit(), 1), ObjectModelEntryFlags::none },
- { "model", OBJECT_MODEL_FUNC((const FopDt *)&self->GetModel()), ObjectModelEntryFlags::none },
- { "monitors", OBJECT_MODEL_FUNC_NOSELF(&monitorsArrayDescriptor), ObjectModelEntryFlags::none },
- { "sensor", OBJECT_MODEL_FUNC((int32_t)self->GetSensorNumber()), ObjectModelEntryFlags::none },
- { "standby", OBJECT_MODEL_FUNC(self->GetStandbyTemperature(), 1), ObjectModelEntryFlags::live },
- { "state", OBJECT_MODEL_FUNC(self->GetStatus().ToString()), ObjectModelEntryFlags::live },
+ { "active", OBJECT_MODEL_FUNC(self->GetActiveTemperature(), 1), ObjectModelEntryFlags::live },
+ { "avgPwm", OBJECT_MODEL_FUNC(self->GetAveragePWM(), 3), ObjectModelEntryFlags::live },
+ { "current", OBJECT_MODEL_FUNC(self->GetTemperature(), 2), ObjectModelEntryFlags::live },
+ { "max", OBJECT_MODEL_FUNC(self->GetHighestTemperatureLimit(), 1), ObjectModelEntryFlags::none },
+ { "maxBadReadings", OBJECT_MODEL_FUNC((int32_t)self->maxBadTemperatureCount), ObjectModelEntryFlags::none },
+ { "maxHeatingFaultTime", OBJECT_MODEL_FUNC(self->maxHeatingFaultTime, 1), ObjectModelEntryFlags::none },
+ { "maxTempExcursion", OBJECT_MODEL_FUNC(self->maxTempExcursion, 1), ObjectModelEntryFlags::none },
+ { "min", OBJECT_MODEL_FUNC(self->GetLowestTemperatureLimit(), 1), ObjectModelEntryFlags::none },
+ { "model", OBJECT_MODEL_FUNC((const FopDt *)&self->GetModel()), ObjectModelEntryFlags::none },
+ { "monitors", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
+ { "sensor", OBJECT_MODEL_FUNC((int32_t)self->GetSensorNumber()), ObjectModelEntryFlags::none },
+ { "standby", OBJECT_MODEL_FUNC(self->GetStandbyTemperature(), 1), ObjectModelEntryFlags::live },
+ { "state", OBJECT_MODEL_FUNC(self->GetStatus().ToString()), ObjectModelEntryFlags::live },
// 1. Heater.monitors[] members
- { "action", OBJECT_MODEL_FUNC_IF(self->monitors[context.GetLastIndex()].GetTrigger() != HeaterMonitorTrigger::Disabled,
- (int32_t)self->monitors[context.GetLastIndex()].GetAction()), ObjectModelEntryFlags::none },
- { "condition", OBJECT_MODEL_FUNC(self->monitors[context.GetLastIndex()].GetTriggerName()), ObjectModelEntryFlags::none },
- { "limit", OBJECT_MODEL_FUNC_IF(self->monitors[context.GetLastIndex()].GetTrigger() != HeaterMonitorTrigger::Disabled,
- self->monitors[context.GetLastIndex()].GetTemperatureLimit(), 1), ObjectModelEntryFlags::none },
+ { "action", OBJECT_MODEL_FUNC_IF(self->monitors[context.GetLastIndex()].GetTrigger() != HeaterMonitorTrigger::Disabled,
+ (int32_t)self->monitors[context.GetLastIndex()].GetAction()), ObjectModelEntryFlags::none },
+ { "condition", OBJECT_MODEL_FUNC(self->monitors[context.GetLastIndex()].GetTriggerName()), ObjectModelEntryFlags::none },
+ { "limit", OBJECT_MODEL_FUNC_IF(self->monitors[context.GetLastIndex()].GetTrigger() != HeaterMonitorTrigger::Disabled,
+ self->monitors[context.GetLastIndex()].GetTemperatureLimit(), 1), ObjectModelEntryFlags::none },
};
-constexpr uint8_t Heater::objectModelTableDescriptor[] = { 2, 10, 3 };
+constexpr uint8_t Heater::objectModelTableDescriptor[] = { 2, 13, 3 };
DEFINE_GET_OBJECT_MODEL_TABLE(Heater)
@@ -89,6 +96,7 @@ float Heater::lastCoolingRate;
FansBitmap Heater::tuningFans;
unsigned int Heater::tuningPhase;
uint8_t Heater::idleCyclesDone;
+bool Heater::tuningQuietMode;
Heater::HeaterParameters Heater::fanOffParams, Heater::fanOnParams;
@@ -105,7 +113,7 @@ Heater::HeaterParameters Heater::fanOffParams, Heater::fanOnParams;
Heater::Heater(unsigned int num) noexcept
: tuned(false), heaterNumber(num), sensorNumber(-1), activeTemperature(0.0), standbyTemperature(0.0),
- maxTempExcursion(DefaultMaxTempExcursion), maxHeatingFaultTime(DefaultMaxHeatingFaultTime),
+ maxTempExcursion(DefaultMaxTempExcursion), maxHeatingFaultTime(DefaultMaxHeatingFaultTime), maxBadTemperatureCount(DefaultMaxBadTemperatureCount),
isBedOrChamber(false),
active(false), modelSetByUser(false), monitorsSetByUser(false)
{
@@ -152,7 +160,7 @@ GCodeResult Heater::SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const
if (gb.Seen('R'))
{
seen = true;
- heatingRate = gb.GetFValue();
+ heatingRate = gb.GetPositiveFValue();
}
gb.TryGetFValue('E', coolingRateExponent, seen);
}
@@ -282,15 +290,17 @@ GCodeResult Heater::StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansB
const float ambientTemp = (seenA) ? gb.GetFValue() : currentTemp;
if (ambientTemp + 20 >= targetTemp)
{
- reply.printf("Target temperature must be at least 20C above ambient temperature");
+ reply.copy("Target temperature must be at least 20C above ambient temperature");
+ return GCodeResult::error;
}
- // Get abd store the optional parameters
+ // Get and store the optional parameters
tuningTargetTemp = targetTemp;
tuningFans = fans;
tuningPwm = (gb.Seen('P')) ? gb.GetLimitedFValue('P', 0.1, 1.0) : GetModel().GetMaxPwm();
tuningHysteresis = (gb.Seen('Y')) ? gb.GetLimitedFValue('Y', 1.0, 20.0) : DefaultTuningHysteresis;
tuningFanPwm = (gb.Seen('F')) ? gb.GetLimitedFValue('F', 0.1, 1.0) : DefaultTuningFanPwm;
+ tuningQuietMode = gb.Seen('Q') && gb.GetUIValue() != 0;
const GCodeResult rslt = StartAutoTune(reply, seenA, ambientTemp);
if (rslt == GCodeResult::ok)
@@ -415,14 +425,23 @@ void Heater::SetAndReportModelAfterTuning(bool usingFans) noexcept
if (Succeeded(rslt))
{
tuned = true;
- str.printf( "Auto tuning heater %u completed after %u idle and %u tuning cycles in %" PRIu32 " seconds. This heater needs the following M307 command:\n ",
+ str.printf( "Auto tuning heater %u completed after %u idle and %u tuning cycles in %" PRIu32 " seconds",
GetHeaterNumber(),
idleCyclesDone,
(usingFans) ? fanOffParams.numCycles + fanOnParams.numCycles : fanOffParams.numCycles,
(millis() - tuningBeginTime)/(uint32_t)SecondsToMillis
);
- GetModel().AppendM307Command(GetHeaterNumber(), str.GetRef(), !reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber()));
+ if (tuningQuietMode)
+ {
+ str.cat('\n');
+ }
+ else
+ {
+ str.cat( ". This heater needs the following M307 command:\n");
+ GetModel().AppendM307Command(GetHeaterNumber(), str.GetRef(), !reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber()));
+ }
reprap.GetPlatform().Message(LoggedGenericMessage, str.c_str());
+
if (reprap.Debug(moduleHeat))
{
str.printf("Long term gain %.1f/%.1f", (double)fanOffParams.GetNormalGain(), (double)fanOffParams.gain);
@@ -434,13 +453,16 @@ void Heater::SetAndReportModelAfterTuning(bool usingFans) noexcept
reprap.GetPlatform().Message(GenericMessage, str.c_str());
}
- if (reprap.GetGCodes().SawM501InConfigFile())
- {
- reprap.GetPlatform().Message(GenericMessage, "Send M500 to save this command in config-override.g\n");
- }
- else
+ if (!tuningQuietMode)
{
- reprap.GetPlatform().MessageF(GenericMessage, "Edit the M307 H%u command in config.g to match this. Omit the V parameter if the heater is not powered from VIN.\n", GetHeaterNumber());
+ if (reprap.GetGCodes().SawM501InConfigFile())
+ {
+ reprap.GetPlatform().Message(GenericMessage, "Send M500 to save this command in config-override.g\n");
+ }
+ else
+ {
+ reprap.GetPlatform().MessageF(GenericMessage, "Edit the M307 H%u command in config.g to match this. Omit the V parameter if the heater is not powered from VIN.\n", GetHeaterNumber());
+ }
}
}
else
@@ -452,13 +474,22 @@ void Heater::SetAndReportModelAfterTuning(bool usingFans) noexcept
}
}
-GCodeResult Heater::SetFaultDetectionParameters(float pMaxTempExcursion, float pMaxFaultTime, const StringRef& reply) noexcept
+GCodeResult Heater::ConfigureFaultDetectionParameters(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- maxTempExcursion = pMaxTempExcursion;
- maxHeatingFaultTime = pMaxFaultTime;
- const GCodeResult rslt = UpdateFaultDetectionParameters(reply);
- reprap.HeatUpdated();
- return rslt;
+ bool seenValue = false;
+ gb.TryGetNonNegativeFValue('P', maxHeatingFaultTime, seenValue);
+ gb.TryGetNonNegativeFValue('T', maxTempExcursion, seenValue);
+ gb.TryGetLimitedUIValue('R', maxBadTemperatureCount, seenValue, 51);
+ if (seenValue)
+ {
+ const GCodeResult rslt = UpdateFaultDetectionParameters(reply);
+ reprap.HeatUpdated();
+ return rslt;
+ }
+
+ reply.printf("Heater %u allowed excursion %.1f" DEGREE_SYMBOL "C, fault trigger time %.1f seconds, max %" PRIu32 " consecutive bad readings",
+ heaterNumber, (double)maxTempExcursion, (double)maxHeatingFaultTime, maxBadTemperatureCount);
+ return GCodeResult::ok;
}
// Process M143 for this heater
@@ -659,6 +690,17 @@ GCodeResult Heater::SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, co
return GCodeResult::ok;
}
+GCodeResult Heater::SetFaultDetectionParameters(const CanMessageSetHeaterFaultDetectionParameters& msg, const StringRef& reply) noexcept
+{
+ maxTempExcursion = msg.maxTempExcursion;
+ maxHeatingFaultTime = msg.maxFaultTime;
+ if (msg.version35)
+ {
+ maxBadTemperatureCount = msg.maxBadTemperatureCount;
+ }
+ return GCodeResult::ok;
+}
+
GCodeResult Heater::SetModel(unsigned int heater, const CanMessageHeaterModelNewNew& msg, const StringRef& reply) noexcept
{
const float temperatureLimit = GetHighestTemperatureLimit();
diff --git a/src/Heating/Heater.h b/src/Heating/Heater.h
index 28c27933..008b9b54 100644
--- a/src/Heating/Heater.h
+++ b/src/Heating/Heater.h
@@ -20,7 +20,6 @@
# include "CanId.h"
#endif
-
#define TUNE_WITH_HALF_FAN 0
class HeaterMonitor;
@@ -32,6 +31,7 @@ struct CanMessageHeaterModelNewNew;
struct CanMessageSetHeaterTemperature;
struct CanMessageSetHeaterMonitors;
struct CanMessageHeaterTuningCommand;
+struct CanMessageSetHeaterFaultDetectionParameters;
#endif
// Enumeration to describe the status of a heater. Note that the web interface returns the numerical values, so don't change them.
@@ -75,11 +75,7 @@ public:
GCodeResult StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException);
// Start an auto tune cycle for this heater
void GetAutoTuneStatus(const StringRef& reply) const noexcept; // Get the auto tune status or last result
-
- void GetFaultDetectionParameters(float& pMaxTempExcursion, float& pMaxFaultTime) const noexcept
- { pMaxTempExcursion = maxTempExcursion; pMaxFaultTime = maxHeatingFaultTime; }
- GCodeResult SetFaultDetectionParameters(float pMaxTempExcursion, float pMaxFaultTime, const StringRef& reply) noexcept;
-
+ GCodeResult ConfigureFaultDetectionParameters(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException);
GCodeResult ConfigureMonitor(GCodeBuffer &gb, const StringRef &reply) THROWS(GCodeException);
float GetHighestTemperatureLimit() const noexcept;
@@ -92,6 +88,7 @@ public:
virtual GCodeResult TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept = 0;
GCodeResult SetModel(unsigned int heater, const CanMessageHeaterModelNewNew& msg, const StringRef& reply) noexcept;
GCodeResult SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept;
+ GCodeResult SetFaultDetectionParameters(const CanMessageSetHeaterFaultDetectionParameters& msg, const StringRef& reply) noexcept;
GCodeResult SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept;
#endif
@@ -112,8 +109,7 @@ public:
#endif
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(monitors)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
struct HeaterParameters
{
@@ -138,6 +134,7 @@ protected:
void SetSensorNumber(int sn) noexcept;
float GetMaxTemperatureExcursion() const noexcept { return maxTempExcursion; }
float GetMaxHeatingFaultTime() const noexcept { return maxHeatingFaultTime; }
+ uint32_t GetMaxBadTemperatureCount() const noexcept { return maxBadTemperatureCount; }
float GetTargetTemperature() const noexcept { return (active) ? activeTemperature : standbyTemperature; }
bool IsBedOrChamber() const noexcept { return isBedOrChamber; }
@@ -187,6 +184,7 @@ protected:
static FansBitmap tuningFans;
static unsigned int tuningPhase;
static uint8_t idleCyclesDone;
+ static bool tuningQuietMode;
static HeaterParameters fanOffParams, fanOnParams;
@@ -202,6 +200,7 @@ private:
float standbyTemperature; // the required standby temperature
float maxTempExcursion; // the maximum temperature excursion permitted while maintaining the setpoint
float maxHeatingFaultTime; // how long a heater fault is permitted to persist before a heater fault is raised
+ uint32_t maxBadTemperatureCount; // the number of consecutive bad sensor readings we allow before raising a fault
bool isBedOrChamber; // true if this was a bed or chamber heater when we were switched on
bool active; // are we active or standby?
diff --git a/src/Heating/HeaterMonitor.cpp b/src/Heating/HeaterMonitor.cpp
index 3f9255a2..a593b072 100644
--- a/src/Heating/HeaterMonitor.cpp
+++ b/src/Heating/HeaterMonitor.cpp
@@ -17,7 +17,7 @@ HeaterMonitor::HeaterMonitor() noexcept
}
// Check if any action needs to be taken. Returns true if everything is OK
-bool HeaterMonitor::Check() noexcept
+bool HeaterMonitor::Check(uint32_t maxBadTemperatureCount) noexcept
{
if (sensorNumber >= 0 && trigger != HeaterMonitorTrigger::Disabled)
{
@@ -27,7 +27,7 @@ bool HeaterMonitor::Check() noexcept
if (err != TemperatureError::success)
{
badTemperatureCount++;
- if (badTemperatureCount > MaxBadTemperatureCount)
+ if (badTemperatureCount > maxBadTemperatureCount)
{
reprap.GetPlatform().MessageF(ErrorMessage, "Temperature reading error on sensor %d\n", sensorNumber);
return false;
diff --git a/src/Heating/HeaterMonitor.h b/src/Heating/HeaterMonitor.h
index 441dbf26..206ea51a 100644
--- a/src/Heating/HeaterMonitor.h
+++ b/src/Heating/HeaterMonitor.h
@@ -48,7 +48,7 @@ public:
void Set(int sn, float lim, HeaterMonitorAction act, HeaterMonitorTrigger trig) noexcept;
void Disable() noexcept;
- bool Check() noexcept; // Check if any action needs to be taken
+ bool Check(uint32_t) noexcept; // Check if any action needs to be taken
int GetSensorNumber() const noexcept { return sensorNumber; } // Get the supervisory sensor number
float GetTemperatureLimit() const noexcept { return limit; } // Get the temperature limit
diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp
index 6b077304..6ea9b866 100644
--- a/src/Heating/LocalHeater.cpp
+++ b/src/Heating/LocalHeater.cpp
@@ -251,7 +251,7 @@ void LocalHeater::Spin() noexcept
{
// Error may be a temporary error and may correct itself after a few additional reads
badTemperatureCount++;
- if (badTemperatureCount > MaxBadTemperatureCount)
+ if (badTemperatureCount > GetMaxBadTemperatureCount())
{
RaiseHeaterFault(HeaterFaultType::failedToReadSensor, "%s", TemperatureErrorString(err));
}
@@ -443,7 +443,7 @@ void LocalHeater::Spin() noexcept
for (size_t i = 0; i < ARRAY_SIZE(monitors); ++i)
{
HeaterMonitor& prot = monitors[i];
- if (!prot.Check())
+ if (!prot.Check(GetMaxBadTemperatureCount()))
{
if (reprap.Debug(moduleHeat))
{
@@ -923,7 +923,7 @@ void LocalHeater::RaiseHeaterFault(HeaterFaultType type, const char *_ecv_array
if (mode != HeaterMode::fault)
{
mode = HeaterMode::fault;
- reprap.FlagTemperatureFault(GetHeaterNumber());
+ Tool::FlagTemperatureFault(GetHeaterNumber());
va_list vargs;
va_start(vargs, format);
diff --git a/src/Heating/RemoteHeater.cpp b/src/Heating/RemoteHeater.cpp
index 931fbcf2..6d6e081a 100644
--- a/src/Heating/RemoteHeater.cpp
+++ b/src/Heating/RemoteHeater.cpp
@@ -426,6 +426,8 @@ GCodeResult RemoteHeater::UpdateFaultDetectionParameters(const StringRef& reply)
msg->heater = GetHeaterNumber();
msg->maxFaultTime = GetMaxHeatingFaultTime();
msg->maxTempExcursion = GetMaxTemperatureExcursion();
+ msg->maxBadTemperatureCount = GetMaxBadTemperatureCount();
+ msg->version35 = true;
return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply);
}
diff --git a/src/Heating/Sensors/AdditionalOutputSensor.cpp b/src/Heating/Sensors/AdditionalOutputSensor.cpp
index b0fe0e47..ca0f1de9 100644
--- a/src/Heating/Sensors/AdditionalOutputSensor.cpp
+++ b/src/Heating/Sensors/AdditionalOutputSensor.cpp
@@ -11,6 +11,10 @@
#include <Heating/Heat.h>
#include <GCodes/GCodeBuffer/GCodeBuffer.h>
+#if SUPPORT_REMOTE_COMMANDS
+# include <CanMessageGenericParser.h>
+#endif
+
AdditionalOutputSensor::AdditionalOutputSensor(unsigned int sensorNum, const char *type, bool pEnforcePollOrder) noexcept
: TemperatureSensor(sensorNum, type), parentSensor(0), outputNumber(0), enforcePollOrder(pEnforcePollOrder)
{
@@ -22,74 +26,101 @@ AdditionalOutputSensor::~AdditionalOutputSensor() noexcept
GCodeResult AdditionalOutputSensor::Configure(GCodeBuffer& gb, const StringRef& reply, bool& changed)
{
+ GCodeResult rslt = GCodeResult::ok;
+
if (gb.Seen('P'))
{
changed = true;
String<StringLength20> pParam;
gb.GetQuotedString(pParam.GetRef());
-
- const char *pn = pParam.c_str();
- if (*pn != 'S' && *pn != 's')
+ rslt = ConfigurePort(pParam.c_str(), reply);
+ if (rslt > GCodeResult::warning)
{
- reply.copy("Parent sensor needs to start with S");
- return GCodeResult::error;
+ return rslt;
}
- // Advance beyond the leading S
- ++pn;
+ }
+
+ TryConfigureSensorName(gb, changed);
+ if (!changed && !gb.Seen('Y'))
+ {
+ // No parameters were provided, so report the current configuration
+ CopyBasicDetails(reply);
+ reply.catf(", additional output %d of sensor %d", outputNumber, parentSensor);
+ }
+ return GCodeResult::ok;
+}
+
+#if SUPPORT_REMOTE_COMMANDS
+
+GCodeResult AdditionalOutputSensor::Configure(const CanMessageGenericParser& parser, const StringRef& reply) noexcept
+{
+ String<StringLength20> pParam;
+ if (parser.GetStringParam('P', pParam.GetRef()))
+ {
+ return ConfigurePort(pParam.c_str(), reply);
+ }
+
+ CopyBasicDetails(reply);
+ reply.catf(", additional output %d of sensor %d", outputNumber, parentSensor);
+ return GCodeResult::ok;
+}
- if (!isDigit(*pn))
+#endif
+
+GCodeResult AdditionalOutputSensor::ConfigurePort(const char* portName, const StringRef& reply) noexcept
+{
+ if (toupper(*portName) != 'S')
+ {
+ reply.copy("Parent sensor needs to start with S");
+ return GCodeResult::error;
+ }
+ // Advance beyond the leading S
+ ++portName;
+
+ if (!isDigit(*portName))
+ {
+ reply.copy("Parent sensor number expected following S");
+ return GCodeResult::error;
+ }
+
+ // Parse parent sensor number
+ parentSensor = StrToU32(portName, &portName);
+ if (*portName != '.')
+ {
+ reply.copy("Missing additional output number of parent");
+ return GCodeResult::error;
+ }
+
+ // We use this block to have the ReadLockedPointer below go out of scope as early as possible
+ {
+ const auto parent = reprap.GetHeat().FindSensor(parentSensor);
+ if (parent.IsNull())
{
- reply.copy("Parent sensor number expected following S");
+ reply.printf("Parent sensor %d does not exist", parentSensor);
return GCodeResult::error;
}
- // Parse parent sensor number
- parentSensor = StrToU32(pn, &pn);
- if (*pn != '.')
+ if (enforcePollOrder && parentSensor > GetSensorNumber())
{
- reply.copy("Missing additional output number of parent");
+ reply.copy("Parent sensor must be a lower sensor number than this one");
return GCodeResult::error;
}
- // We use this block to have the ReadLockPointer below go out of scope as early as possible
+ // Advance beyond the dot
+ ++portName;
+
+ // Parse output number
+ outputNumber = StrToU32(portName, &portName);
+
+ if (outputNumber > parent->GetNumAdditionalOutputs())
{
- const auto parent = reprap.GetHeat().FindSensor(parentSensor);
- if (parent.IsNull())
- {
- reply.printf("Parent sensor %d does not exist", parentSensor);
- return GCodeResult::error;
- }
-
- if (enforcePollOrder && parentSensor > GetSensorNumber())
- {
- reply.copy("Parent sensor must be a lower sensor number than this one");
- return GCodeResult::error;
- }
-
- // Advance beyond the dot
- ++pn;
-
- // Parse output number
- outputNumber = StrToU32(pn, &pn);
-
- if (outputNumber > parent->GetNumAdditionalOutputs())
- {
- reply.printf("Parent sensor only has %d additional outputs", parent->GetNumAdditionalOutputs());
- return GCodeResult::error;
- }
+ reply.printf("Parent sensor only has %d additional outputs", parent->GetNumAdditionalOutputs());
+ return GCodeResult::error;
}
-
- // Initialize with a value already
- Poll();
}
- TryConfigureSensorName(gb, changed);
- if (!changed && !gb.Seen('Y'))
- {
- // No parameters were provided, so report the current configuration
- CopyBasicDetails(reply);
- reply.catf(", additional output %d of sensor %d", outputNumber, parentSensor);
- }
+ // Initialize with a value already
+ Poll();
return GCodeResult::ok;
}
diff --git a/src/Heating/Sensors/AdditionalOutputSensor.h b/src/Heating/Sensors/AdditionalOutputSensor.h
index 3fbdf32d..5a8f7ae0 100644
--- a/src/Heating/Sensors/AdditionalOutputSensor.h
+++ b/src/Heating/Sensors/AdditionalOutputSensor.h
@@ -15,7 +15,12 @@ class AdditionalOutputSensor : public TemperatureSensor
public:
AdditionalOutputSensor(unsigned int sensorNum, const char *type, bool pEnforcePollOrder) noexcept;
virtual ~AdditionalOutputSensor() noexcept;
+
GCodeResult Configure(GCodeBuffer& gb, const StringRef& reply, bool& changed) override THROWS(GCodeException);
+#if SUPPORT_REMOTE_COMMANDS
+ GCodeResult Configure(const CanMessageGenericParser& parser, const StringRef& reply) noexcept override;
+#endif
+
void Poll() noexcept override;
protected:
@@ -23,6 +28,8 @@ protected:
uint8_t outputNumber;
private:
+ GCodeResult ConfigurePort(const char* portName, const StringRef& reply) noexcept;
+
bool enforcePollOrder;
};
diff --git a/src/Heating/Sensors/BME280.cpp b/src/Heating/Sensors/BME280.cpp
new file mode 100644
index 00000000..a52617be
--- /dev/null
+++ b/src/Heating/Sensors/BME280.cpp
@@ -0,0 +1,788 @@
+/*
+ * BME280.cpp
+ *
+ * Created on: 10 Sept 2022
+ * Author: David
+ *
+ * Portions of this driver are derived from code at https://github.com/BoschSensortec/BME280_driver. The following applies to those portions:
+ *
+ * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved.
+ *
+ * BSD-3-Clause
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "BME280.h"
+
+#if SUPPORT_BME280
+
+constexpr uint16_t MinimumReadInterval = 1000; // ms
+constexpr uint32_t BME280_Frequency = 4000000; // maximum for BME280 is 10MHz
+constexpr SpiMode BME280_SpiMode = SPI_MODE_0; // BME280 does mode 0 or mode 3 depending on value of CLK at falling edge of CS
+constexpr size_t MaxRegistersToRead = 26;
+
+// BME280 support functions, derived from code at https://github.com/BoschSensortec/BME280_driver
+
+/**\name Internal macros */
+/* To identify osr settings selected by user */
+constexpr uint8_t OVERSAMPLING_SETTINGS = 0x07;
+
+/* To identify filter and standby settings selected by user */
+constexpr uint8_t FILTER_STANDBY_SETTINGS = 0x18;
+
+/*!
+ * @brief This API is the entry point. It reads the chip-id and calibration data from the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_init() noexcept
+{
+ /* chip id read try count */
+ uint8_t try_count = 5;
+
+ while (try_count)
+ {
+ /* Read the chip-id of bme280 sensor */
+ static_assert(1 <= MaxRegistersToRead);
+ uint8_t chip_id = 0;
+ TemperatureError rslt = bme280_get_regs(BME280_CHIP_ID_ADDR, &chip_id, 1);
+
+ /* Check for chip id validity */
+ if ((rslt == TemperatureError::success) && (chip_id == BME280_CHIP_ID))
+ {
+ dev.chip_id = chip_id;
+
+ /* Reset the sensor */
+ rslt = bme280_soft_reset();
+
+ if (rslt == TemperatureError::success)
+ {
+ /* Read the calibration data */
+ rslt = get_calib_data();
+ return rslt;
+ }
+
+ break;
+ }
+
+ /* Wait for at least 1 ms */
+ delay(2);
+ --try_count;
+ }
+
+ return TemperatureError::hardwareError;
+}
+
+/*!
+ * @brief This API reads the data from the given register address of the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint16_t len) const noexcept
+{
+ uint8_t addrBuff[MaxRegistersToRead + 1]; // only the first byte is used but the remainder need to be value addresses
+ uint8_t dataBuff[MaxRegistersToRead + 1];
+
+ /* Read the data */
+ addrBuff[0] = reg_addr | 0x80; // for SPI bit 7 is set to read, clear to write
+ TemperatureError err = DoSpiTransaction(addrBuff, dataBuff, len + 1);
+
+ /* Check for communication error */
+ if (err == TemperatureError::success)
+ {
+ memcpy(reg_data, &dataBuff[1], len);
+ }
+ return err;
+}
+
+/*!
+ * @brief This API writes the given data to the register address of the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_set_reg(uint8_t reg_addr, uint8_t reg_data) const noexcept
+{
+ uint8_t temp_buff[2] = { (uint8_t)(reg_addr & 0x7F), reg_data };
+ return DoSpiTransaction(temp_buff, nullptr, 2);
+}
+
+/*!
+ * @brief This API sets the oversampling, filter and standby duration (normal mode) settings in the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_set_sensor_settings(uint8_t desired_settings) const noexcept
+{
+ uint8_t sensor_mode;
+ TemperatureError rslt = bme280_get_sensor_mode(&sensor_mode);
+
+ if ((rslt == TemperatureError::success) && (sensor_mode != BME280_SLEEP_MODE))
+ {
+ rslt = put_device_to_sleep();
+ }
+
+ if (rslt == TemperatureError::success)
+ {
+ /* Check if user wants to change oversampling settings */
+ if (are_settings_changed(OVERSAMPLING_SETTINGS, desired_settings))
+ {
+ rslt = set_osr_settings(desired_settings, &dev.settings);
+ }
+
+ /* Check if user wants to change filter and/or standby settings */
+ if ((rslt == TemperatureError::success) && are_settings_changed(FILTER_STANDBY_SETTINGS, desired_settings))
+ {
+ rslt = set_filter_standby_settings(desired_settings, &dev.settings);
+ }
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This API sets the power mode of the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_set_sensor_mode(uint8_t sensor_mode) const noexcept
+{
+ uint8_t last_set_mode;
+ TemperatureError rslt = bme280_get_sensor_mode(&last_set_mode);
+
+ /* If the sensor is not in sleep mode put the device to sleep
+ * mode
+ */
+ if ((rslt == TemperatureError::success) && (last_set_mode != BME280_SLEEP_MODE))
+ {
+ rslt = put_device_to_sleep();
+ }
+
+ /* Set the power mode */
+ if (rslt == TemperatureError::success)
+ {
+ rslt = write_power_mode(sensor_mode);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This API gets the power mode of the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_get_sensor_mode(uint8_t *sensor_mode) const noexcept
+{
+ /* Read the power mode register */
+ TemperatureError rslt = bme280_get_regs(BME280_PWR_CTRL_ADDR, sensor_mode, 1);
+
+ /* Assign the power mode in the device structure */
+ *sensor_mode = BME280_GET_BITS_POS_0(*sensor_mode, BME280_SENSOR_MODE);
+
+ return rslt;
+}
+
+/*!
+ * @brief This API performs the soft reset of the sensor.
+ */
+TemperatureError BME280TemperatureSensor::bme280_soft_reset() const noexcept
+{
+ /* Write the soft reset command to the sensor */
+ TemperatureError rslt = bme280_set_reg(BME280_RESET_ADDR, BME280_SOFT_RESET_COMMAND);
+ if (rslt == TemperatureError::success)
+ {
+ uint8_t status_reg = 0;
+ uint8_t try_run = 5;
+
+ /* If NVM not copied yet, Wait for NVM to copy */
+ do
+ {
+ /* As per data sheet - Table 1, startup time is 2 ms. */
+ delay(3);
+ static_assert(1 <= MaxRegistersToRead);
+ rslt = bme280_get_regs(BME280_STATUS_REG_ADDR, &status_reg, 1);
+ } while ((rslt == TemperatureError::success) && (try_run--) && (status_reg & BME280_STATUS_IM_UPDATE));
+
+ if (status_reg & BME280_STATUS_IM_UPDATE)
+ {
+ rslt = TemperatureError::hardwareError;
+ }
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This API reads the pressure, temperature and humidity data from the sensor,
+ * compensates the data and store it in the bme280_data structure instance passed by the user.
+ */
+TemperatureError BME280TemperatureSensor::bme280_get_sensor_data() noexcept
+{
+ /* Array to store the pressure, temperature and humidity data read from the sensor */
+ uint8_t reg_data[BME280_P_T_H_DATA_LEN] = { 0 };
+ struct bme280_uncomp_data uncomp_data = { 0 };
+
+ /* Read the pressure and temperature data from the sensor */
+ static_assert(BME280_P_T_H_DATA_LEN <= MaxRegistersToRead);
+ const TemperatureError rslt = bme280_get_regs(BME280_DATA_ADDR, reg_data, BME280_P_T_H_DATA_LEN);
+
+ if (rslt == TemperatureError::success)
+ {
+ /* Parse the read data from the sensor */
+ bme280_parse_sensor_data(reg_data, &uncomp_data);
+
+ /* Compensate the pressure and/or temperature and/or humidity data from the sensor */
+ bme280_compensate_data(&uncomp_data);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This internal API fills the filter settings provided by the user
+ * in the data buffer so as to write in the sensor.
+ */
+static void fill_filter_settings(uint8_t *reg_data, const struct bme280_settings *settings)
+{
+ *reg_data = BME280_SET_BITS(*reg_data, BME280_FILTER, settings->filter);
+}
+
+/*!
+ * @brief This internal API fills the standby duration settings provided by
+ * the user in the data buffer so as to write in the sensor.
+ */
+static void fill_standby_settings(uint8_t *reg_data, const struct bme280_settings *settings)
+{
+ *reg_data = BME280_SET_BITS(*reg_data, BME280_STANDBY, settings->standby_time);
+}
+
+/*!
+ * @brief This internal API fills the pressure oversampling settings provided by
+ * the user in the data buffer so as to write in the sensor.
+ */
+static void fill_osr_press_settings(uint8_t *reg_data, const struct bme280_settings *settings)
+{
+ *reg_data = BME280_SET_BITS(*reg_data, BME280_CTRL_PRESS, settings->osr_p);
+}
+
+/*!
+ * @brief This internal API fills the temperature oversampling settings
+ * provided by the user in the data buffer so as to write in the sensor.
+ */
+static void fill_osr_temp_settings(uint8_t *reg_data, const struct bme280_settings *settings)
+{
+ *reg_data = BME280_SET_BITS(*reg_data, BME280_CTRL_TEMP, settings->osr_t);
+}
+
+/*!
+ * @brief This internal API sets the filter and/or standby duration settings
+ * in the sensor according to the settings selected by the user.
+ */
+TemperatureError BME280TemperatureSensor::set_filter_standby_settings(uint8_t desired_settings, const struct bme280_settings *settings) const noexcept
+{
+ uint8_t reg_data;
+ TemperatureError rslt = bme280_get_regs(BME280_CONFIG_ADDR, &reg_data, 1);
+
+ if (rslt == TemperatureError::success)
+ {
+ if (desired_settings & BME280_FILTER_SEL)
+ {
+ fill_filter_settings(&reg_data, settings);
+ }
+
+ if (desired_settings & BME280_STANDBY_SEL)
+ {
+ fill_standby_settings(&reg_data, settings);
+ }
+
+ /* Write the oversampling settings in the register */
+ rslt = bme280_set_reg(BME280_CONFIG_ADDR, reg_data);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This internal API parse the oversampling(pressure, temperature and humidity),
+ * filter and standby duration settings and store in the device structure.
+ */
+static void parse_device_settings(const uint8_t *reg_data, struct bme280_settings *settings)
+{
+ settings->osr_h = BME280_GET_BITS_POS_0(reg_data[0], BME280_CTRL_HUM);
+ settings->osr_p = BME280_GET_BITS(reg_data[2], BME280_CTRL_PRESS);
+ settings->osr_t = BME280_GET_BITS(reg_data[2], BME280_CTRL_TEMP);
+ settings->filter = BME280_GET_BITS(reg_data[3], BME280_FILTER);
+ settings->standby_time = BME280_GET_BITS(reg_data[3], BME280_STANDBY);
+}
+
+/*!
+ * @brief This internal API writes the power mode in the sensor.
+ */
+TemperatureError BME280TemperatureSensor::write_power_mode(uint8_t sensor_mode) const noexcept
+{
+ /* Variable to store the value read from power mode register */
+ uint8_t sensor_mode_reg_val;
+
+ /* Read the power mode register */
+ TemperatureError rslt = bme280_get_regs(BME280_PWR_CTRL_ADDR, &sensor_mode_reg_val, 1);
+
+ /* Set the power mode */
+ if (rslt == TemperatureError::success)
+ {
+ sensor_mode_reg_val = BME280_SET_BITS_POS_0(sensor_mode_reg_val, BME280_SENSOR_MODE, sensor_mode);
+
+ /* Write the power mode in the register */
+ rslt = bme280_set_reg(BME280_PWR_CTRL_ADDR, sensor_mode_reg_val);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This internal API puts the device to sleep mode.
+ */
+TemperatureError BME280TemperatureSensor::put_device_to_sleep() const noexcept
+{
+ uint8_t reg_data[4];
+ TemperatureError rslt = bme280_get_regs(BME280_CTRL_HUM_ADDR, reg_data, 4);
+
+ if (rslt == TemperatureError::success)
+ {
+ struct bme280_settings settings;
+ parse_device_settings(reg_data, &settings);
+ rslt = bme280_soft_reset();
+
+ if (rslt == TemperatureError::success)
+ {
+ rslt = reload_device_settings(&settings);
+ }
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This internal API reloads the already existing device settings in the sensor after soft reset.
+ */
+TemperatureError BME280TemperatureSensor::reload_device_settings(const struct bme280_settings *settings) const noexcept
+{
+ TemperatureError rslt = set_osr_settings(BME280_ALL_SETTINGS_SEL, settings);
+ if (rslt == TemperatureError::success)
+ {
+ rslt = set_filter_standby_settings(BME280_ALL_SETTINGS_SEL, settings);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This internal API is used to compensate the raw temperature data and return the compensated temperature data in float data type.
+ */
+float BME280TemperatureSensor::compensate_temperature(const bme280_uncomp_data *uncomp_data) noexcept
+{
+ constexpr float temperature_min = -40;
+ constexpr float temperature_max = 85;
+
+ float var1 = ((float)uncomp_data->temperature) / 16384.0 - ((float)dev.calib_data.dig_t1) / 1024.0;
+ var1 = var1 * ((float)dev.calib_data.dig_t2);
+ float var2 = (((float)uncomp_data->temperature) / 131072.0 - ((float)dev.calib_data.dig_t1) / 8192.0);
+ var2 = (var2 * var2) * ((float)dev.calib_data.dig_t3);
+ dev.calib_data.t_fine = (int32_t)(var1 + var2);
+ float temperature = (var1 + var2) / 5120.0;
+ temperature = constrain<float>(temperature, temperature_min, temperature_max);
+
+ return temperature;
+}
+
+/*!
+ * @brief This internal API is used to compensate the raw pressure data and return the compensated pressure data in float data type.
+ */
+float BME280TemperatureSensor::compensate_pressure(const bme280_uncomp_data *uncomp_data) const noexcept
+{
+ constexpr float pressure_min = 30000.0;
+ constexpr float pressure_max = 110000.0;
+
+ float var1 = ((float)dev.calib_data.t_fine / 2.0) - 64000.0;
+ float var2 = var1 * var1 * ((float)dev.calib_data.dig_p6) / 32768.0;
+ var2 = var2 + var1 * ((float)dev.calib_data.dig_p5) * 2.0;
+ var2 = (var2 / 4.0) + (((float)dev.calib_data.dig_p4) * 65536.0);
+ const float var3 = ((float)dev.calib_data.dig_p3) * var1 * var1 / 524288.0;
+ var1 = (var3 + ((float)dev.calib_data.dig_p2) * var1) / 524288.0;
+ var1 = (1.0 + var1 / 32768.0) * ((float)dev.calib_data.dig_p1);
+ float pressure;
+
+ /* avoid exception caused by division by zero */
+ if (var1 > (0.0))
+ {
+ pressure = 1048576.0 - (float) uncomp_data->pressure;
+ pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1;
+ var1 = ((float)dev.calib_data.dig_p9) * pressure * pressure / 2147483648.0;
+ var2 = pressure * ((float)dev.calib_data.dig_p8) / 32768.0;
+ pressure = pressure + (var1 + var2 + ((float)dev.calib_data.dig_p7)) / 16.0;
+ pressure = constrain<float>(pressure, pressure_min, pressure_max);
+ }
+ else /* Invalid case */
+ {
+ pressure = pressure_min;
+ }
+
+ return pressure * 0.01; // we want pressure in hectopascals, not pascals
+}
+
+/*!
+ * @brief This internal API is used to compensate the raw humidity data and return the compensated humidity data in float data type.
+ */
+float BME280TemperatureSensor::compensate_humidity(const bme280_uncomp_data *uncomp_data) const noexcept
+{
+ constexpr float humidity_min = 0.0;
+ constexpr float humidity_max = 100.0;
+
+ const float var1 = ((float)dev.calib_data.t_fine) - 76800.0;
+ const float var2 = (((float)dev.calib_data.dig_h4) * 64.0 + (((float)dev.calib_data.dig_h5) / 16384.0) * var1);
+ const float var3 = uncomp_data->humidity - var2;
+ const float var4 = ((float)dev.calib_data.dig_h2) / 65536.0;
+ const float var5 = (1.0 + (((float)dev.calib_data.dig_h3) / 67108864.0) * var1);
+ float var6 = 1.0 + (((float)dev.calib_data.dig_h6) / 67108864.0) * var1 * var5;
+ var6 = var3 * var4 * (var5 * var6);
+ float humidity = var6 * (1.0 - ((float)dev.calib_data.dig_h1) * var6 / 524288.0);
+ humidity = constrain<float>(humidity, humidity_min, humidity_max);
+ return humidity;
+}
+
+/*!
+ * @brief This API is used to parse the pressure, temperature and humidity data and store it in the bme280_uncomp_data structure instance.
+ */
+void BME280TemperatureSensor::bme280_parse_sensor_data(const uint8_t *reg_data, bme280_uncomp_data *uncomp_data) noexcept
+{
+ /* Variables to store the sensor data */
+ uint32_t data_xlsb;
+ uint32_t data_lsb;
+ uint32_t data_msb;
+
+ /* Store the parsed register values for pressure data */
+ data_msb = (uint32_t)reg_data[0] << 12;
+ data_lsb = (uint32_t)reg_data[1] << 4;
+ data_xlsb = (uint32_t)reg_data[2] >> 4;
+ uncomp_data->pressure = data_msb | data_lsb | data_xlsb;
+
+ /* Store the parsed register values for temperature data */
+ data_msb = (uint32_t)reg_data[3] << 12;
+ data_lsb = (uint32_t)reg_data[4] << 4;
+ data_xlsb = (uint32_t)reg_data[5] >> 4;
+ uncomp_data->temperature = data_msb | data_lsb | data_xlsb;
+
+ /* Store the parsed register values for humidity data */
+ data_msb = (uint32_t)reg_data[6] << 8;
+ data_lsb = (uint32_t)reg_data[7];
+ uncomp_data->humidity = data_msb | data_lsb;
+}
+
+/*!
+ * @brief This API is used to compensate the pressure and/or temperature and/or humidity data according to the component selected by the user.
+ */
+void BME280TemperatureSensor::bme280_compensate_data(const bme280_uncomp_data *uncomp_data) noexcept
+{
+ /* Compensate the temperature data */
+ compTemperature = compensate_temperature(uncomp_data);
+ /* Compensate the pressure data */
+ compPressure = compensate_pressure(uncomp_data);
+ /* Compensate the humidity data */
+ compHumidity = compensate_humidity(uncomp_data);
+}
+
+/*!
+ * @brief This internal API sets the oversampling settings for pressure,
+ * temperature and humidity in the sensor.
+ */
+TemperatureError BME280TemperatureSensor::set_osr_settings(uint8_t desired_settings, const bme280_settings *settings) const noexcept
+{
+ TemperatureError rslt = TemperatureError::success;
+
+ if (desired_settings & BME280_OSR_HUM_SEL)
+ {
+ rslt = set_osr_humidity_settings(settings);
+ }
+
+ if (desired_settings & (BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL))
+ {
+ rslt = set_osr_press_temp_settings(desired_settings, settings);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This API sets the humidity oversampling settings of the sensor.
+ */
+TemperatureError BME280TemperatureSensor::set_osr_humidity_settings(const struct bme280_settings *settings) const noexcept
+{
+ uint8_t ctrl_hum = settings->osr_h & BME280_CTRL_HUM_MSK;
+
+ /* Write the humidity control value in the register */
+ TemperatureError rslt = bme280_set_reg(BME280_CTRL_HUM_ADDR, ctrl_hum);
+
+ /* Humidity related changes will be only effective after a write operation to ctrl_meas register */
+ if (rslt == TemperatureError::success)
+ {
+ uint8_t ctrl_meas;
+ rslt = bme280_get_regs(BME280_CTRL_MEAS_ADDR, &ctrl_meas, 1);
+ if (rslt == TemperatureError::success)
+ {
+ rslt = bme280_set_reg(BME280_CTRL_MEAS_ADDR, ctrl_meas);
+ }
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This API sets the pressure and/or temperature oversampling settings in the sensor according to the settings selected by the user. */
+TemperatureError BME280TemperatureSensor::set_osr_press_temp_settings(uint8_t desired_settings, const struct bme280_settings *settings) const noexcept
+{
+ uint8_t reg_data;
+ TemperatureError rslt = bme280_get_regs(BME280_CTRL_MEAS_ADDR, &reg_data, 1);
+
+ if (rslt == TemperatureError::success)
+ {
+ if (desired_settings & BME280_OSR_PRESS_SEL)
+ {
+ fill_osr_press_settings(&reg_data, settings);
+ }
+
+ if (desired_settings & BME280_OSR_TEMP_SEL)
+ {
+ fill_osr_temp_settings(&reg_data, settings);
+ }
+
+ /* Write the oversampling settings in the register */
+ rslt = bme280_set_reg(BME280_CTRL_MEAS_ADDR, reg_data);
+ }
+
+ return rslt;
+}
+
+/*!
+ * @brief This internal API is used to parse the temperature and pressure calibration data and store it in device structure.
+ */
+void BME280TemperatureSensor::parse_temp_press_calib_data(const uint8_t *reg_data) noexcept
+{
+ dev.calib_data.dig_t1 = BME280_CONCAT_BYTES(reg_data[1], reg_data[0]);
+ dev.calib_data.dig_t2 = (int16_t)BME280_CONCAT_BYTES(reg_data[3], reg_data[2]);
+ dev.calib_data.dig_t3 = (int16_t)BME280_CONCAT_BYTES(reg_data[5], reg_data[4]);
+ dev.calib_data.dig_p1 = BME280_CONCAT_BYTES(reg_data[7], reg_data[6]);
+ dev.calib_data.dig_p2 = (int16_t)BME280_CONCAT_BYTES(reg_data[9], reg_data[8]);
+ dev.calib_data.dig_p3 = (int16_t)BME280_CONCAT_BYTES(reg_data[11], reg_data[10]);
+ dev.calib_data.dig_p4 = (int16_t)BME280_CONCAT_BYTES(reg_data[13], reg_data[12]);
+ dev.calib_data.dig_p5 = (int16_t)BME280_CONCAT_BYTES(reg_data[15], reg_data[14]);
+ dev.calib_data.dig_p6 = (int16_t)BME280_CONCAT_BYTES(reg_data[17], reg_data[16]);
+ dev.calib_data.dig_p7 = (int16_t)BME280_CONCAT_BYTES(reg_data[19], reg_data[18]);
+ dev.calib_data.dig_p8 = (int16_t)BME280_CONCAT_BYTES(reg_data[21], reg_data[20]);
+ dev.calib_data.dig_p9 = (int16_t)BME280_CONCAT_BYTES(reg_data[23], reg_data[22]);
+ dev.calib_data.dig_h1 = reg_data[25];
+}
+
+/*!
+ * @brief This internal API is used to parse the humidity calibration data and store it in device structure.
+ */
+void BME280TemperatureSensor::parse_humidity_calib_data(const uint8_t *reg_data) noexcept
+{
+ dev.calib_data.dig_h2 = (int16_t)BME280_CONCAT_BYTES(reg_data[1], reg_data[0]);
+ dev.calib_data.dig_h3 = reg_data[2];
+ const int16_t dig_h4_msb = (int16_t)(int8_t)reg_data[3] * 16;
+ const int16_t dig_h4_lsb = (int16_t)(reg_data[4] & 0x0F);
+ dev.calib_data.dig_h4 = dig_h4_msb | dig_h4_lsb;
+ const int16_t dig_h5_msb = (int16_t)(int8_t)reg_data[5] * 16;
+ const int16_t dig_h5_lsb = (int16_t)(reg_data[4] >> 4);
+ dev.calib_data.dig_h5 = dig_h5_msb | dig_h5_lsb;
+ dev.calib_data.dig_h6 = (int8_t)reg_data[6];
+}
+
+/*!
+ * @brief This internal API is used to identify the settings which the user
+ * wants to modify in the sensor.
+ */
+/*static*/ bool BME280TemperatureSensor::are_settings_changed(uint8_t sub_settings, uint8_t desired_settings) noexcept
+{
+ return (sub_settings & desired_settings);
+}
+
+/*!
+ * @brief This internal API reads the calibration data from the sensor, parse it and store in the device structure.
+ */
+TemperatureError BME280TemperatureSensor::get_calib_data() noexcept
+{
+ /* Array to store calibration data */
+ uint8_t temp_calib_data[BME280_TEMP_PRESS_CALIB_DATA_LEN] = { 0 };
+
+ /* Read the calibration data from the sensor */
+ static_assert(BME280_TEMP_PRESS_CALIB_DATA_LEN <= MaxRegistersToRead);
+ TemperatureError rslt = bme280_get_regs(BME280_TEMP_PRESS_CALIB_DATA_ADDR, temp_calib_data, BME280_TEMP_PRESS_CALIB_DATA_LEN);
+
+ if (rslt == TemperatureError::success)
+ {
+ /* Parse temperature and pressure calibration data and store it in device structure */
+ parse_temp_press_calib_data(temp_calib_data);
+
+ /* Read the humidity calibration data from the sensor */
+ static_assert(BME280_HUMIDITY_CALIB_DATA_LEN <= MaxRegistersToRead);
+ rslt = bme280_get_regs(BME280_HUMIDITY_CALIB_DATA_ADDR, temp_calib_data, BME280_HUMIDITY_CALIB_DATA_LEN);
+
+ if (rslt == TemperatureError::success)
+ {
+ /* Parse humidity calibration data and store it in device structure */
+ parse_humidity_calib_data(temp_calib_data);
+ }
+ }
+
+ return rslt;
+}
+
+// BME280TemperatureSensor members
+
+BME280TemperatureSensor::BME280TemperatureSensor(unsigned int sensorNum) noexcept
+ : SpiTemperatureSensor(sensorNum, "BME280 temperature", BME280_SpiMode, BME280_Frequency)
+{
+}
+
+GCodeResult BME280TemperatureSensor::Configure(GCodeBuffer &gb, const StringRef &reply, bool &changed)
+{
+ if (!ConfigurePort(gb, reply, changed))
+ {
+ return GCodeResult::error;
+ }
+ TryConfigureSensorName(gb, changed);
+ return FinishConfiguring(changed, reply);
+}
+
+#if SUPPORT_REMOTE_COMMANDS
+
+GCodeResult BME280TemperatureSensor::Configure(const CanMessageGenericParser& parser, const StringRef& reply) noexcept
+{
+ bool seen = false;
+ if (!ConfigurePort(parser, reply, seen))
+ {
+ return GCodeResult::error;
+ }
+ return FinishConfiguring(seen, reply);
+}
+
+#endif
+
+GCodeResult BME280TemperatureSensor::FinishConfiguring(bool changed, const StringRef& reply) noexcept
+{
+ if (changed)
+ {
+ // Initialise the sensor
+ InitSpi();
+ TemperatureError rslt = bme280_init();
+ SetResult(0.0, rslt);
+
+ if (rslt == TemperatureError::success)
+ {
+ /* Recommended mode of operation: Indoor navigation */
+ dev.settings.osr_h = BME280_OVERSAMPLING_1X;
+ dev.settings.osr_p = BME280_OVERSAMPLING_16X;
+ dev.settings.osr_t = BME280_OVERSAMPLING_2X;
+ dev.settings.filter = BME280_FILTER_COEFF_16;
+ dev.settings.standby_time = BME280_STANDBY_TIME_62_5_MS;
+
+ uint8_t settings_sel = BME280_OSR_PRESS_SEL;
+ settings_sel |= BME280_OSR_TEMP_SEL;
+ settings_sel |= BME280_OSR_HUM_SEL;
+ settings_sel |= BME280_STANDBY_SEL;
+ settings_sel |= BME280_FILTER_SEL;
+ rslt = bme280_set_sensor_settings(settings_sel);
+ if (rslt == TemperatureError::success)
+ {
+ rslt = bme280_set_sensor_mode(BME280_NORMAL_MODE);
+ }
+ }
+
+ if (rslt != TemperatureError::success)
+ {
+ reply.printf("Failed to initialise BME280 sensor: %s", TemperatureErrorString(rslt));
+ return GCodeResult::error;
+ }
+ }
+ else
+ {
+ CopyBasicDetails(reply);
+ }
+ return GCodeResult::ok;
+}
+
+TemperatureError BME280TemperatureSensor::GetLatestTemperature(float &t, uint8_t outputNumber) noexcept
+{
+ if (outputNumber > 2)
+ {
+ t = BadErrorTemperature;
+ return TemperatureError::invalidOutputNumber;
+ }
+
+ const auto result = TemperatureSensor::GetLatestTemperature(t);
+ if (outputNumber == 1)
+ {
+ t = compPressure;
+ }
+ else if (outputNumber == 2)
+ {
+ t = compHumidity;
+ }
+ return result;
+}
+
+void BME280TemperatureSensor::Poll() noexcept
+{
+ const auto now = millis();
+ if ((now - GetLastReadingTime()) >= MinimumReadInterval)
+ {
+ if (bme280_get_sensor_data() == TemperatureError::success)
+ {
+ SetResult(compTemperature, TemperatureError::success);
+ }
+ else
+ {
+ SetResult(TemperatureError::hardwareError);
+ }
+ }
+}
+
+// Bme280PressureSensor members
+
+BME280PressureSensor::BME280PressureSensor(unsigned int sensorNum) noexcept
+ : AdditionalOutputSensor(sensorNum, "BME280-pressure", false)
+{
+}
+
+BME280PressureSensor::~BME280PressureSensor() noexcept
+{
+}
+
+// Bme280HumiditySensor members
+
+BME280HumiditySensor::BME280HumiditySensor(unsigned int sensorNum) noexcept
+ : AdditionalOutputSensor(sensorNum, "BME280-humidity", false)
+{
+}
+
+BME280HumiditySensor::~BME280HumiditySensor() noexcept
+{
+}
+
+#endif
+
+// End
diff --git a/src/Heating/Sensors/BME280.h b/src/Heating/Sensors/BME280.h
new file mode 100644
index 00000000..ea05456e
--- /dev/null
+++ b/src/Heating/Sensors/BME280.h
@@ -0,0 +1,94 @@
+/*
+ * BME280.h
+ *
+ * Created on: 10 Sept 2022
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_SENSORS_BME280_H_
+#define SRC_HEATING_SENSORS_BME280_H_
+
+#include "SpiTemperatureSensor.h"
+
+#if SUPPORT_BME280
+
+#include "AdditionalOutputSensor.h"
+#include "bme280_defs.h"
+
+class BME280TemperatureSensor : public SpiTemperatureSensor
+{
+public:
+ BME280TemperatureSensor(unsigned int sensorNum) noexcept;
+
+ GCodeResult Configure(GCodeBuffer& gb, const StringRef& reply, bool& changed) override THROWS(GCodeException);
+#if SUPPORT_REMOTE_COMMANDS
+ GCodeResult Configure(const CanMessageGenericParser& parser, const StringRef& reply) noexcept override;
+#endif
+
+ const uint8_t GetNumAdditionalOutputs() const noexcept override { return 2; }
+ TemperatureError GetLatestTemperature(float& t, uint8_t outputNumber = 0) noexcept override;
+ void Poll() noexcept override;
+ const char *GetShortSensorType() const noexcept override { return TypeName; }
+
+ static constexpr const char *TypeName = "bme280";
+
+private:
+ TemperatureError bme280_init() noexcept;
+ TemperatureError bme280_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint16_t len) const noexcept;
+ TemperatureError bme280_set_reg(uint8_t reg_addr, uint8_t reg_data) const noexcept;
+ TemperatureError bme280_set_sensor_settings(uint8_t desired_settings) const noexcept;
+ TemperatureError bme280_set_sensor_mode(uint8_t sensor_mode) const noexcept;
+ TemperatureError bme280_get_sensor_mode(uint8_t *sensor_mode) const noexcept;
+ TemperatureError get_calib_data() noexcept;
+ TemperatureError bme280_soft_reset() const noexcept;
+ TemperatureError set_filter_standby_settings(uint8_t desired_settings, const struct bme280_settings *settings) const noexcept;
+ TemperatureError write_power_mode(uint8_t sensor_mode) const noexcept;
+ TemperatureError put_device_to_sleep() const noexcept;
+ TemperatureError reload_device_settings(const struct bme280_settings *settings) const noexcept;
+ TemperatureError bme280_get_sensor_data() noexcept;
+ TemperatureError set_osr_settings(uint8_t desired_settings, const struct bme280_settings *settings) const noexcept;
+ TemperatureError set_osr_humidity_settings(const struct bme280_settings *settings) const noexcept;
+ TemperatureError set_osr_press_temp_settings(uint8_t desired_settings, const struct bme280_settings *settings) const noexcept;
+ float compensate_temperature(const struct bme280_uncomp_data *uncomp_data) noexcept;
+ float compensate_pressure(const struct bme280_uncomp_data *uncomp_data) const noexcept;
+ float compensate_humidity(const struct bme280_uncomp_data *uncomp_data) const noexcept;
+ void bme280_compensate_data(const bme280_uncomp_data *uncomp_data) noexcept;
+ void parse_temp_press_calib_data(const uint8_t *reg_data) noexcept;
+ void parse_humidity_calib_data(const uint8_t *reg_data) noexcept;
+ void bme280_parse_sensor_data(const uint8_t *reg_data, struct bme280_uncomp_data *uncomp_data) noexcept;
+ static bool are_settings_changed(uint8_t sub_settings, uint8_t desired_settings) noexcept;
+ GCodeResult FinishConfiguring(bool changed, const StringRef& reply) noexcept;
+
+ bme280_dev dev;
+ float compPressure; /*< Compensated pressure */
+ float compTemperature; /*< Compensated temperature */
+ float compHumidity; /*< Compensated humidity */
+};
+
+// This class represents a DHT humidity sensor
+class BME280PressureSensor : public AdditionalOutputSensor
+{
+public:
+ BME280PressureSensor(unsigned int sensorNum) noexcept;
+ ~BME280PressureSensor() noexcept;
+
+ const char *GetShortSensorType() const noexcept override { return TypeName; }
+
+ static constexpr const char *TypeName = "bmepressure";
+};
+
+// This class represents a DHT humidity sensor
+class BME280HumiditySensor : public AdditionalOutputSensor
+{
+public:
+ BME280HumiditySensor(unsigned int sensorNum) noexcept;
+ ~BME280HumiditySensor() noexcept;
+
+ const char *GetShortSensorType() const noexcept override { return TypeName; }
+
+ static constexpr const char *TypeName = "bmehumidity";
+};
+
+#endif // SUPPORT_BME280
+
+#endif /* SRC_HEATING_SENSORS_BME280_H_ */
diff --git a/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp b/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp
index 24100285..af2ecc42 100644
--- a/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp
+++ b/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp
@@ -77,7 +77,7 @@ GCodeResult CurrentLoopTemperatureSensor::FinishConfiguring(bool changed, const
for (unsigned int i = 0; i < 3; ++i) // try 3 times
{
rslt = TryGetLinearAdcTemperature(t);
- if (lastResult == TemperatureError::success)
+ if (rslt == TemperatureError::success)
{
break;
}
diff --git a/src/Heating/Sensors/DhtSensor.cpp b/src/Heating/Sensors/DhtSensor.cpp
index 748593fc..9c55cb72 100644
--- a/src/Heating/Sensors/DhtSensor.cpp
+++ b/src/Heating/Sensors/DhtSensor.cpp
@@ -27,7 +27,7 @@ void DhtDataTransition(CallbackParameter cp) noexcept
// Class DhtTemperatureSensor members
DhtTemperatureSensor::DhtTemperatureSensor(unsigned int sensorNum, DhtSensorType t) noexcept
- : SensorWithPort(sensorNum, "DHT-temperature"), type(t), lastReadTime(0)
+ : SensorWithPort(sensorNum, "DHT-temperature"), type(t)
{
}
@@ -159,7 +159,7 @@ void DhtTemperatureSensor::Interrupt() noexcept
void DhtTemperatureSensor::Poll() noexcept
{
const auto now = millis();
- if ((now - lastReadTime) >= MinimumReadInterval)
+ if ((now - GetLastReadingTime()) >= MinimumReadInterval)
{
TakeReading();
}
@@ -214,7 +214,7 @@ void DhtTemperatureSensor::TakeReading() noexcept
SetResult(t, rslt);
badTemperatureCount = 0;
}
- else if (badTemperatureCount < MaxBadTemperatureCount)
+ else if (badTemperatureCount < MaxDhtBadTemperatureCount)
{
badTemperatureCount++;
}
@@ -223,7 +223,6 @@ void DhtTemperatureSensor::TakeReading() noexcept
SetResult(rslt);
lastHumidity = BadErrorTemperature;
}
- lastReadTime = millis();
}
// Process a reading. If success then update the temperature and humidity and return TemperatureError::success.
diff --git a/src/Heating/Sensors/DhtSensor.h b/src/Heating/Sensors/DhtSensor.h
index 2c17421a..9befde18 100644
--- a/src/Heating/Sensors/DhtSensor.h
+++ b/src/Heating/Sensors/DhtSensor.h
@@ -14,7 +14,7 @@
# include "AdditionalOutputSensor.h"
# include "SensorWithPort.h"
-# include "RTOSIface/RTOSIface.h"
+# include <RTOSIface/RTOSIface.h>
enum class DhtSensorType
{
@@ -30,6 +30,10 @@ public:
~DhtTemperatureSensor() noexcept;
GCodeResult Configure(GCodeBuffer& gb, const StringRef& reply, bool& changed) override THROWS(GCodeException);
+#if 0 //SUPPORT_REMOTE_COMMANDS
+ GCodeResult Configure(const CanMessageGenericParser& parser, const StringRef& reply) noexcept override;
+#endif
+
TemperatureError GetLatestTemperature(float& t, uint8_t outputNumber = 0) noexcept override;
const uint8_t GetNumAdditionalOutputs() const noexcept override { return 1; }
void Poll() noexcept override;
@@ -54,7 +58,6 @@ private:
float lastHumidity;
uint8_t badTemperatureCount;
- uint32_t lastReadTime;
volatile uint16_t lastPulseTime;
volatile uint8_t numPulses;
uint16_t pulses[41]; // 1 start bit + 40 data bits
diff --git a/src/Heating/Sensors/RtdSensor31865.cpp b/src/Heating/Sensors/RtdSensor31865.cpp
index 81475821..1639e492 100644
--- a/src/Heating/Sensors/RtdSensor31865.cpp
+++ b/src/Heating/Sensors/RtdSensor31865.cpp
@@ -85,7 +85,7 @@ GCodeResult RtdSensor31865::Configure(GCodeBuffer& gb, const StringRef& reply, b
if (gb.Seen('R'))
{
changed = true;
- rrefTimes100 = lrintf(gb.GetFValue() * 100);
+ rrefTimes100 = lrintf(gb.GetPositiveFValue() * 100);
}
return FinishConfiguring(changed, reply);
@@ -159,10 +159,7 @@ GCodeResult RtdSensor31865::FinishConfiguring(bool changed, const StringRef& rep
delay(MinimumReadInterval);
}
- lastReadingTime = millis();
- lastResult = rslt;
- lastTemperature = 0.0;
-
+ SetResult(0.0, rslt);
if (rslt != TemperatureError::success)
{
reply.printf("Failed to initialise RTD: %s", TemperatureErrorString(rslt));
diff --git a/src/Heating/Sensors/SpiTemperatureSensor.cpp b/src/Heating/Sensors/SpiTemperatureSensor.cpp
index f511b580..5e3e40e2 100644
--- a/src/Heating/Sensors/SpiTemperatureSensor.cpp
+++ b/src/Heating/Sensors/SpiTemperatureSensor.cpp
@@ -10,7 +10,7 @@
#if SUPPORT_SPI_SENSORS
#include <Platform/Tasks.h>
-#include <Hardware/SharedSpi/SharedSpiDevice.h>
+#include <Hardware/Spi/SharedSpiDevice.h>
SpiTemperatureSensor::SpiTemperatureSensor(unsigned int sensorNum, const char *name, SpiMode spiMode, uint32_t clockFrequency) noexcept
: SensorWithPort(sensorNum, name), device(SharedSpiDevice::GetMainSharedSpiDevice(), clockFrequency, spiMode, NoPin, false)
@@ -18,8 +18,7 @@ SpiTemperatureSensor::SpiTemperatureSensor(unsigned int sensorNum, const char *n
#if defined(__LPC17xx__)
device.sspChannel = TempSensorSSPChannel; // use SSP0 on LPC
#endif
- lastTemperature = 0.0;
- lastResult = TemperatureError::notInitialised;
+ SetResult(0.0, TemperatureError::notInitialised);
}
bool SpiTemperatureSensor::ConfigurePort(GCodeBuffer& gb, const StringRef& reply, bool& seen)
@@ -42,7 +41,6 @@ bool SpiTemperatureSensor::ConfigurePort(const CanMessageGenericParser& parser,
void SpiTemperatureSensor::InitSpi() noexcept
{
- lastReadingTime = millis();
}
// Send and receive 1 to 8 bytes of data and return the result as a single 32-bit word
@@ -76,6 +74,29 @@ TemperatureError SpiTemperatureSensor::DoSpiTransaction(const uint8_t dataOut[],
return TemperatureError::success;
}
+// Send and receive data
+TemperatureError SpiTemperatureSensor::DoSpiTransaction(const uint8_t dataOut[], uint8_t dataIn[], size_t nbytes) const noexcept
+{
+ if (!device.Select(10))
+ {
+ return TemperatureError::busBusy;
+ }
+
+ delayMicroseconds(1);
+ const bool ok = device.TransceivePacket(dataOut, dataIn, nbytes);
+ delayMicroseconds(1);
+
+ device.Deselect();
+ delayMicroseconds(1);
+
+ if (!ok)
+ {
+ return TemperatureError::timeout;
+ }
+
+ return TemperatureError::success;
+}
+
#endif // SUPPORT_SPI_SENSORS
// End
diff --git a/src/Heating/Sensors/SpiTemperatureSensor.h b/src/Heating/Sensors/SpiTemperatureSensor.h
index 52909d71..3b6f5247 100644
--- a/src/Heating/Sensors/SpiTemperatureSensor.h
+++ b/src/Heating/Sensors/SpiTemperatureSensor.h
@@ -12,7 +12,7 @@
#if SUPPORT_SPI_SENSORS
-#include <Hardware/SharedSpi/SharedSpiClient.h>
+#include <Hardware/Spi/SharedSpiClient.h>
class SpiTemperatureSensor : public SensorWithPort
{
@@ -28,11 +28,9 @@ protected:
void InitSpi() noexcept;
TemperatureError DoSpiTransaction(const uint8_t dataOut[], size_t nbytes, uint32_t& rslt) const noexcept
pre(nbytes <= 8);
+ TemperatureError DoSpiTransaction(const uint8_t dataOut[], uint8_t dataIn[], size_t nbytes) const noexcept;
SharedSpiClient device;
- uint32_t lastReadingTime;
- float lastTemperature;
- TemperatureError lastResult;
};
#endif // SUPPORT_SPI_SENSORS
diff --git a/src/Heating/Sensors/TemperatureSensor.cpp b/src/Heating/Sensors/TemperatureSensor.cpp
index 768db7eb..266af18e 100644
--- a/src/Heating/Sensors/TemperatureSensor.cpp
+++ b/src/Heating/Sensors/TemperatureSensor.cpp
@@ -6,6 +6,7 @@
#include "CurrentLoopTemperatureSensor.h"
#include "LinearAnalogSensor.h"
#include "RemoteSensor.h"
+#include "BME280.h"
#include "GCodes/GCodeBuffer/GCodeBuffer.h"
#if SUPPORT_REMOTE_COMMANDS
@@ -66,7 +67,10 @@ TemperatureSensor::~TemperatureSensor() noexcept
// Return the latest temperature reading
TemperatureError TemperatureSensor::GetLatestTemperature(float& t, uint8_t outputNumber) noexcept
{
- if (millis() - whenLastRead > TemperatureReadingTimeout)
+ // We must read whenLastRead *before* we call millis(). Otherwise, a task switch to the heater task could occur after we call millis and before we read whenLastRead,
+ // so that when we read whenLastRead its value is greater than the result from millis().
+ const uint32_t wlr = whenLastRead;
+ if (millis() - wlr > TemperatureReadingTimeout)
{
lastTemperature = BadErrorTemperature;
lastResult = TemperatureError::timeout;
@@ -235,6 +239,20 @@ TemperatureSensor *TemperatureSensor::Create(unsigned int sensorNum, const char
ts = new DhtHumiditySensor(sensorNum);
}
#endif
+#if SUPPORT_BME280
+ else if (ReducedStringEquals(typeName, BME280TemperatureSensor::TypeName))
+ {
+ ts = new BME280TemperatureSensor(sensorNum);
+ }
+ else if (ReducedStringEquals(typeName, BME280PressureSensor::TypeName))
+ {
+ ts = new BME280PressureSensor(sensorNum);
+ }
+ else if (ReducedStringEquals(typeName, BME280HumiditySensor::TypeName))
+ {
+ ts = new BME280HumiditySensor(sensorNum);
+ }
+#endif
#if HAS_CPU_TEMP_SENSOR
else if (ReducedStringEquals(typeName, CpuTemperatureSensor::TypeName))
{
diff --git a/src/Heating/Sensors/TemperatureSensor.h b/src/Heating/Sensors/TemperatureSensor.h
index 8b58d7ff..ce3843e8 100644
--- a/src/Heating/Sensors/TemperatureSensor.h
+++ b/src/Heating/Sensors/TemperatureSensor.h
@@ -82,6 +82,9 @@ public:
virtual void UpdateRemoteTemperature(CanAddress src, const CanSensorReport& report) noexcept;
#endif
+ // Get the time of the last reading
+ uint32_t GetLastReadingTime() const noexcept { return whenLastRead; }
+
// Factory method
#if SUPPORT_CAN_EXPANSION
static TemperatureSensor *Create(unsigned int sensorNum, CanAddress boardAddress, const char *typeName, const StringRef& reply) noexcept;
diff --git a/src/Heating/Sensors/ThermocoupleSensor31855.cpp b/src/Heating/Sensors/ThermocoupleSensor31855.cpp
index 38da9524..ef1a42b9 100644
--- a/src/Heating/Sensors/ThermocoupleSensor31855.cpp
+++ b/src/Heating/Sensors/ThermocoupleSensor31855.cpp
@@ -106,7 +106,6 @@ GCodeResult ThermocoupleSensor31855::FinishConfiguring(bool changed, const Strin
{
// Initialise the sensor
InitSpi();
- lastReadingTime = millis();
}
else
{
@@ -128,7 +127,7 @@ void ThermocoupleSensor31855::Poll() noexcept
if ((rawVal & 0x00020008) != 0)
{
// These two bits should always read 0. Likely the entire read was 0xFF 0xFF which is not uncommon when first powering up
- lastResult = TemperatureError::ioError;
+ SetResult(TemperatureError::ioError);
}
else if ((rawVal & 0x00010007) != 0) // check the fault bits
{
@@ -164,7 +163,7 @@ void ThermocoupleSensor31855::Poll() noexcept
{
// Fault indicator was set but a fault reason was not set (nbits == 0) or too many fault reason bits were set (nbits > 1).
// Assume that a communication error with the MAX31855 has occurred.
- lastResult = TemperatureError::ioError;
+ SetResult(TemperatureError::ioError);
}
}
}
diff --git a/src/Heating/Sensors/ThermocoupleSensor31856.cpp b/src/Heating/Sensors/ThermocoupleSensor31856.cpp
index c4e9441e..42991ffd 100644
--- a/src/Heating/Sensors/ThermocoupleSensor31856.cpp
+++ b/src/Heating/Sensors/ThermocoupleSensor31856.cpp
@@ -168,10 +168,7 @@ GCodeResult ThermocoupleSensor31856::FinishConfiguring(bool changed, const Strin
delay(MinimumReadInterval);
}
- lastReadingTime = millis();
- lastResult = rslt;
- lastTemperature = 0.0;
-
+ SetResult(0.0, rslt);
if (rslt != TemperatureError::success)
{
reply.printf("Failed to initialise thermocouple: %s", TemperatureErrorString(rslt));
@@ -226,7 +223,6 @@ void ThermocoupleSensor31856::Poll() noexcept
}
else
{
- lastReadingTime = millis();
if ((rawVal & 0x00FF) != 0)
{
// One or more fault bits is set
diff --git a/src/Heating/Sensors/bme280_defs.h b/src/Heating/Sensors/bme280_defs.h
new file mode 100644
index 00000000..ec76ac36
--- /dev/null
+++ b/src/Heating/Sensors/bme280_defs.h
@@ -0,0 +1,482 @@
+/**
+* Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved.
+*
+* BSD-3-Clause
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* 3. Neither the name of the copyright holder nor the names of its
+* contributors may be used to endorse or promote products derived from
+* this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*
+* @file bme280_defs.h
+* @date 2020-03-28
+* @version v3.5.0
+*
+*/
+
+#ifndef BME280_DEFS_H_
+#define BME280_DEFS_H_
+
+/********************************************************/
+/* header includes */
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/kernel.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif
+
+/********************************************************/
+/*! @name Common macros */
+/********************************************************/
+
+#if !defined(UINT8_C) && !defined(INT8_C)
+#define INT8_C(x) S8_C(x)
+#define UINT8_C(x) U8_C(x)
+#endif
+
+#if !defined(UINT16_C) && !defined(INT16_C)
+#define INT16_C(x) S16_C(x)
+#define UINT16_C(x) U16_C(x)
+#endif
+
+#if !defined(INT32_C) && !defined(UINT32_C)
+#define INT32_C(x) S32_C(x)
+#define UINT32_C(x) U32_C(x)
+#endif
+
+#if !defined(INT64_C) && !defined(UINT64_C)
+#define INT64_C(x) S64_C(x)
+#define UINT64_C(x) U64_C(x)
+#endif
+
+/**@}*/
+/**\name C standard macros */
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *) 0)
+#endif
+#endif
+
+/********************************************************/
+
+#ifndef BME280_64BIT_ENABLE /*< Check if 64-bit integer (using BME280_64BIT_ENABLE) is enabled */
+#ifndef BME280_32BIT_ENABLE /*< Check if 32-bit integer (using BME280_32BIT_ENABLE) is enabled */
+#ifndef BME280_FLOAT_ENABLE /*< If any of the integer data types not enabled then enable BME280_FLOAT_ENABLE */
+#define BME280_FLOAT_ENABLE
+#endif
+#endif
+#endif
+
+#ifndef TRUE
+#define TRUE UINT8_C(1)
+#endif
+#ifndef FALSE
+#define FALSE UINT8_C(0)
+#endif
+
+/**
+ * BME280_INTF_RET_TYPE is the read/write interface return type which can be overwritten by the build system.
+ */
+#ifndef BME280_INTF_RET_TYPE
+#define BME280_INTF_RET_TYPE int8_t
+#endif
+
+/**
+ * The last error code from read/write interface is stored in the device structure as intf_rslt.
+ */
+#ifndef BME280_INTF_RET_SUCCESS
+#define BME280_INTF_RET_SUCCESS INT8_C(0)
+#endif
+
+/**\name I2C addresses */
+#define BME280_I2C_ADDR_PRIM UINT8_C(0x76)
+#define BME280_I2C_ADDR_SEC UINT8_C(0x77)
+
+/**\name BME280 chip identifier */
+#define BME280_CHIP_ID UINT8_C(0x60)
+
+/**\name Register Address */
+#define BME280_CHIP_ID_ADDR UINT8_C(0xD0)
+#define BME280_RESET_ADDR UINT8_C(0xE0)
+#define BME280_TEMP_PRESS_CALIB_DATA_ADDR UINT8_C(0x88)
+#define BME280_HUMIDITY_CALIB_DATA_ADDR UINT8_C(0xE1)
+#define BME280_PWR_CTRL_ADDR UINT8_C(0xF4)
+#define BME280_CTRL_HUM_ADDR UINT8_C(0xF2)
+#define BME280_CTRL_MEAS_ADDR UINT8_C(0xF4)
+#define BME280_CONFIG_ADDR UINT8_C(0xF5)
+#define BME280_DATA_ADDR UINT8_C(0xF7)
+
+#if 0
+
+/**\name API success code */
+#define BME280_OK INT8_C(0)
+
+/**\name API error codes */
+#define BME280_E_NULL_PTR INT8_C(-1)
+#define BME280_E_DEV_NOT_FOUND INT8_C(-2)
+#define BME280_E_INVALID_LEN INT8_C(-3)
+#define BME280_E_COMM_FAIL INT8_C(-4)
+#define BME280_E_SLEEP_MODE_FAIL INT8_C(-5)
+#define BME280_E_NVM_COPY_FAILED INT8_C(-6)
+
+/**\name API warning codes */
+#define BME280_W_INVALID_OSR_MACRO INT8_C(1)
+
+#endif
+
+/**\name Macros related to size */
+#define BME280_TEMP_PRESS_CALIB_DATA_LEN UINT8_C(26)
+#define BME280_HUMIDITY_CALIB_DATA_LEN UINT8_C(7)
+#define BME280_P_T_H_DATA_LEN UINT8_C(8)
+
+/**\name Sensor power modes */
+#define BME280_SLEEP_MODE UINT8_C(0x00)
+#define BME280_FORCED_MODE UINT8_C(0x01)
+#define BME280_NORMAL_MODE UINT8_C(0x03)
+
+/**\name Macro to combine two 8 bit data's to form a 16 bit data */
+#define BME280_CONCAT_BYTES(msb, lsb) (((uint16_t)msb << 8) | (uint16_t)lsb)
+
+#define BME280_SET_BITS(reg_data, bitname, data) \
+ ((reg_data & ~(bitname##_MSK)) | \
+ ((data << bitname##_POS) & bitname##_MSK))
+#define BME280_SET_BITS_POS_0(reg_data, bitname, data) \
+ ((reg_data & ~(bitname##_MSK)) | \
+ (data & bitname##_MSK))
+
+#define BME280_GET_BITS(reg_data, bitname) ((reg_data & (bitname##_MSK)) >> \
+ (bitname##_POS))
+#define BME280_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK))
+
+/**\name Macros for bit masking */
+#define BME280_SENSOR_MODE_MSK UINT8_C(0x03)
+#define BME280_SENSOR_MODE_POS UINT8_C(0x00)
+
+#define BME280_CTRL_HUM_MSK UINT8_C(0x07)
+#define BME280_CTRL_HUM_POS UINT8_C(0x00)
+
+#define BME280_CTRL_PRESS_MSK UINT8_C(0x1C)
+#define BME280_CTRL_PRESS_POS UINT8_C(0x02)
+
+#define BME280_CTRL_TEMP_MSK UINT8_C(0xE0)
+#define BME280_CTRL_TEMP_POS UINT8_C(0x05)
+
+#define BME280_FILTER_MSK UINT8_C(0x1C)
+#define BME280_FILTER_POS UINT8_C(0x02)
+
+#define BME280_STANDBY_MSK UINT8_C(0xE0)
+#define BME280_STANDBY_POS UINT8_C(0x05)
+
+/**\name Sensor component selection macros
+ * These values are internal for API implementation. Don't relate this to
+ * data sheet.
+ */
+#define BME280_PRESS UINT8_C(1)
+#define BME280_TEMP UINT8_C(1 << 1)
+#define BME280_HUM UINT8_C(1 << 2)
+#define BME280_ALL UINT8_C(0x07)
+
+/**\name Settings selection macros */
+#define BME280_OSR_PRESS_SEL UINT8_C(1)
+#define BME280_OSR_TEMP_SEL UINT8_C(1 << 1)
+#define BME280_OSR_HUM_SEL UINT8_C(1 << 2)
+#define BME280_FILTER_SEL UINT8_C(1 << 3)
+#define BME280_STANDBY_SEL UINT8_C(1 << 4)
+#define BME280_ALL_SETTINGS_SEL UINT8_C(0x1F)
+
+/**\name Oversampling macros */
+#define BME280_NO_OVERSAMPLING UINT8_C(0x00)
+#define BME280_OVERSAMPLING_1X UINT8_C(0x01)
+#define BME280_OVERSAMPLING_2X UINT8_C(0x02)
+#define BME280_OVERSAMPLING_4X UINT8_C(0x03)
+#define BME280_OVERSAMPLING_8X UINT8_C(0x04)
+#define BME280_OVERSAMPLING_16X UINT8_C(0x05)
+
+/**\name Measurement delay calculation macros */
+#define BME280_MEAS_OFFSET UINT16_C(1250)
+#define BME280_MEAS_DUR UINT16_C(2300)
+#define BME280_PRES_HUM_MEAS_OFFSET UINT16_C(575)
+#define BME280_MEAS_SCALING_FACTOR UINT16_C(1000)
+
+/**\name Standby duration selection macros */
+#define BME280_STANDBY_TIME_0_5_MS (0x00)
+#define BME280_STANDBY_TIME_62_5_MS (0x01)
+#define BME280_STANDBY_TIME_125_MS (0x02)
+#define BME280_STANDBY_TIME_250_MS (0x03)
+#define BME280_STANDBY_TIME_500_MS (0x04)
+#define BME280_STANDBY_TIME_1000_MS (0x05)
+#define BME280_STANDBY_TIME_10_MS (0x06)
+#define BME280_STANDBY_TIME_20_MS (0x07)
+
+/**\name Filter coefficient selection macros */
+#define BME280_FILTER_COEFF_OFF (0x00)
+#define BME280_FILTER_COEFF_2 (0x01)
+#define BME280_FILTER_COEFF_4 (0x02)
+#define BME280_FILTER_COEFF_8 (0x03)
+#define BME280_FILTER_COEFF_16 (0x04)
+
+#define BME280_STATUS_REG_ADDR (0xF3)
+#define BME280_SOFT_RESET_COMMAND (0xB6)
+#define BME280_STATUS_IM_UPDATE (0x01)
+
+/*!
+ * @brief Interface selection Enums
+ */
+enum bme280_intf {
+ /*< SPI interface */
+ BME280_SPI_INTF,
+ /*< I2C interface */
+ BME280_I2C_INTF
+};
+
+/*!
+ * @brief Type definitions
+ */
+
+#if 0
+/*!
+ * @brief Bus communication function pointer which should be mapped to
+ * the platform specific read functions of the user
+ *
+ * @param[in] reg_addr : Register address from which data is read.
+ * @param[out] reg_data : Pointer to data buffer where read data is stored.
+ * @param[in] len : Number of bytes of data to be read.
+ * @param[in, out] intf_ptr : Void pointer that can enable the linking of descriptors
+ * for interface related call backs.
+ *
+ * @retval 0 -> Success.
+ * @retval Non zero value -> Fail.
+ *
+ */
+typedef BME280_INTF_RET_TYPE (*bme280_read_fptr_t)(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr);
+
+/*!
+ * @brief Bus communication function pointer which should be mapped to
+ * the platform specific write functions of the user
+ *
+ * @param[in] reg_addr : Register address to which the data is written.
+ * @param[in] reg_data : Pointer to data buffer in which data to be written
+ * is stored.
+ * @param[in] len : Number of bytes of data to be written.
+ * @param[in, out] intf_ptr : Void pointer that can enable the linking of descriptors
+ * for interface related call backs
+ *
+ * @retval 0 -> Success.
+ * @retval Non zero value -> Fail.
+ *
+ */
+typedef BME280_INTF_RET_TYPE (*bme280_write_fptr_t)(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len,
+ void *intf_ptr);
+
+/*!
+ * @brief Delay function pointer which should be mapped to
+ * delay function of the user
+ *
+ * @param[in] period : Delay in microseconds.
+ * @param[in, out] intf_ptr : Void pointer that can enable the linking of descriptors
+ * for interface related call backs
+ *
+ */
+typedef void (*bme280_delay_us_fptr_t)(uint32_t period, void *intf_ptr);
+#endif
+
+/*!
+ * @brief Calibration data
+ */
+struct bme280_calib_data
+{
+ /*< Calibration coefficient for the temperature sensor */
+ uint16_t dig_t1;
+
+ /*< Calibration coefficient for the temperature sensor */
+ int16_t dig_t2;
+
+ /*< Calibration coefficient for the temperature sensor */
+ int16_t dig_t3;
+
+ /*< Calibration coefficient for the pressure sensor */
+ uint16_t dig_p1;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p2;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p3;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p4;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p5;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p6;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p7;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p8;
+
+ /*< Calibration coefficient for the pressure sensor */
+ int16_t dig_p9;
+
+ /*< Calibration coefficient for the humidity sensor */
+ uint8_t dig_h1;
+
+ /*< Calibration coefficient for the humidity sensor */
+ int16_t dig_h2;
+
+ /*< Calibration coefficient for the humidity sensor */
+ uint8_t dig_h3;
+
+ /*< Calibration coefficient for the humidity sensor */
+ int16_t dig_h4;
+
+ /*< Calibration coefficient for the humidity sensor */
+ int16_t dig_h5;
+
+ /*< Calibration coefficient for the humidity sensor */
+ int8_t dig_h6;
+
+ /*< Variable to store the intermediate temperature coefficient */
+ int32_t t_fine;
+};
+
+#if 0
+
+/*!
+ * @brief bme280 sensor structure which comprises of temperature, pressure and
+ * humidity data
+ */
+#ifdef BME280_FLOAT_ENABLE
+struct bme280_data
+{
+ /*< Compensated pressure */
+ double pressure;
+
+ /*< Compensated temperature */
+ double temperature;
+
+ /*< Compensated humidity */
+ double humidity;
+};
+#else
+struct bme280_data
+{
+ /*< Compensated pressure */
+ uint32_t pressure;
+
+ /*< Compensated temperature */
+ int32_t temperature;
+
+ /*< Compensated humidity */
+ uint32_t humidity;
+};
+#endif /*! BME280_USE_FLOATING_POINT */
+
+#endif
+
+/*!
+ * @brief bme280 sensor structure which comprises of uncompensated temperature,
+ * pressure and humidity data
+ */
+struct bme280_uncomp_data
+{
+ /*< un-compensated pressure */
+ uint32_t pressure;
+
+ /*< un-compensated temperature */
+ uint32_t temperature;
+
+ /*< un-compensated humidity */
+ uint32_t humidity;
+};
+
+/*!
+ * @brief bme280 sensor settings structure which comprises of mode,
+ * oversampling and filter settings.
+ */
+struct bme280_settings
+{
+ /*< pressure oversampling */
+ uint8_t osr_p;
+
+ /*< temperature oversampling */
+ uint8_t osr_t;
+
+ /*< humidity oversampling */
+ uint8_t osr_h;
+
+ /*< filter coefficient */
+ uint8_t filter;
+
+ /*< standby time */
+ uint8_t standby_time;
+};
+
+/*!
+ * @brief bme280 device structure
+ */
+struct bme280_dev
+{
+ /*< Chip Id */
+ uint8_t chip_id;
+
+#if 0
+ /*< Interface function pointer used to enable the device address for I2C and chip selection for SPI */
+ void *intf_ptr;
+
+ /*< Interface Selection
+ * For SPI, intf = BME280_SPI_INTF
+ * For I2C, intf = BME280_I2C_INTF
+ * */
+ enum bme280_intf intf;
+
+ /*< Read function pointer */
+ bme280_read_fptr_t read;
+
+ /*< Write function pointer */
+ bme280_write_fptr_t write;
+
+ /*< Delay function pointer */
+ bme280_delay_us_fptr_t delay_us;
+#endif
+
+ /*< Trim data */
+ struct bme280_calib_data calib_data;
+
+ /*< Sensor settings */
+ struct bme280_settings settings;
+
+ /*< Variable to store result of read/write function */
+ BME280_INTF_RET_TYPE intf_rslt;
+};
+
+#endif /* BME280_DEFS_H_ */
diff --git a/src/Libraries/Fatfs/ff.h b/src/Libraries/Fatfs/ff.h
index 06afa97d..282fd9d3 100644
--- a/src/Libraries/Fatfs/ff.h
+++ b/src/Libraries/Fatfs/ff.h
@@ -305,18 +305,18 @@ typedef enum {
/*--------------------------------------------------------------*/
/* FatFs module application interface */
-FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode) noexcept; /* Open or create a file */
+FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode) noexcept; /* Open or create a file */
FRESULT f_close (FIL* fp) noexcept; /* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br) noexcept; /* Read data from the file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw) noexcept; /* Write data to the file */
-FRESULT f_lseek (FIL* fp, FSIZE_t ofs) noexcept; /* Move file pointer of the file object */
+FRESULT f_lseek (FIL* fp, FSIZE_t ofs) noexcept; /* Move file pointer of the file object */
FRESULT f_truncate (FIL* fp) noexcept; /* Truncate the file */
FRESULT f_sync (FIL* fp) noexcept; /* Flush cached data of the writing file */
-FRESULT f_opendir (DIR* dp, const TCHAR* path) noexcept; /* Open a directory */
+FRESULT f_opendir (DIR* dp, const TCHAR* path) noexcept; /* Open a directory */
FRESULT f_closedir (DIR* dp) noexcept; /* Close an open directory */
FRESULT f_readdir (DIR* dp, FILINFO* fno) noexcept; /* Read a directory item */
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern) noexcept; /* Find first file */
-FRESULT f_findnext (DIR* dp, FILINFO* fno) noexcept; /* Find next file */
+FRESULT f_findnext (DIR* dp, FILINFO* fno) noexcept; /* Find next file */
FRESULT f_mkdir (const TCHAR* path) noexcept; /* Create a sub directory */
FRESULT f_unlink (const TCHAR* path) noexcept; /* Delete an existing file or directory */
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new) noexcept; /* Rename/Move a file or directory */
@@ -336,7 +336,7 @@ FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len) n
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work) noexcept; /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp) noexcept; /* Set current code page */
int f_putc (TCHAR c, FIL* fp) noexcept; /* Put a character to the file */
-int f_puts (const TCHAR* str, FIL* cp) noexcept; /* Put a string to the file */
+int f_puts (const TCHAR* str, FIL* cp) noexcept; /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...) noexcept; /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp) noexcept; /* Get a string from the file */
diff --git a/src/Libraries/sd_mmc/sd_mmc_spi.cpp b/src/Libraries/sd_mmc/sd_mmc_spi.cpp
index 2df31a90..f7f6a3de 100644
--- a/src/Libraries/sd_mmc/sd_mmc_spi.cpp
+++ b/src/Libraries/sd_mmc/sd_mmc_spi.cpp
@@ -53,8 +53,8 @@
#include "sd_mmc.h"
#include <cstring>
-#include <Hardware/SharedSpi/SharedSpiClient.h>
-#include <Hardware/SharedSpi/SharedSpiDevice.h>
+#include <Hardware/Spi/SharedSpiClient.h>
+#include <Hardware/Spi/SharedSpiDevice.h>
#include <General/Portability.h>
// Enable debug information for SD/MMC SPI module
diff --git a/src/Movement/AxisShaper.cpp b/src/Movement/AxisShaper.cpp
index 55de9ad0..d5ea5eee 100644
--- a/src/Movement/AxisShaper.cpp
+++ b/src/Movement/AxisShaper.cpp
@@ -21,29 +21,33 @@
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(AxisShaper, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(...) OBJECT_MODEL_FUNC_IF_BODY(AxisShaper, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor AxisShaper::amplitudesArrayDescriptor =
+constexpr ObjectModelArrayTableEntry AxisShaper::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((const AxisShaper*)self)->numExtraImpulses; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept
- -> ExpressionValue { return ExpressionValue(((const AxisShaper*)self)->coefficients[context.GetIndex(0)], 3); }
+ // 0. Amplitudes
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((const AxisShaper*)self)->numExtraImpulses; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept
+ -> ExpressionValue { return ExpressionValue(((const AxisShaper*)self)->coefficients[context.GetLastIndex()], 3); }
+ },
+ // 1. Durations
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((const AxisShaper*)self)->numExtraImpulses; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept
+ -> ExpressionValue { return ExpressionValue(((const AxisShaper*)self)->durations[context.GetLastIndex()] * (1.0/StepClockRate), 5); }
+ }
};
-constexpr ObjectModelArrayDescriptor AxisShaper::durationsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((const AxisShaper*)self)->numExtraImpulses; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept
- -> ExpressionValue { return ExpressionValue(((const AxisShaper*)self)->durations[context.GetIndex(0)] * (1.0/StepClockRate), 5); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(AxisShaper)
constexpr ObjectModelTableEntry AxisShaper::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. InputShaper members
- { "amplitudes", OBJECT_MODEL_FUNC_NOSELF(&amplitudesArrayDescriptor), ObjectModelEntryFlags::none },
+ { "amplitudes", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
{ "damping", OBJECT_MODEL_FUNC(self->zeta, 2), ObjectModelEntryFlags::none },
- { "durations", OBJECT_MODEL_FUNC_NOSELF(&durationsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "durations", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
{ "frequency", OBJECT_MODEL_FUNC(self->frequency, 2), ObjectModelEntryFlags::none },
{ "minAcceleration", OBJECT_MODEL_FUNC(self->minimumAcceleration, 1), ObjectModelEntryFlags::none },
{ "type", OBJECT_MODEL_FUNC(self->type.ToString()), ObjectModelEntryFlags::none },
@@ -87,7 +91,7 @@ GCodeResult AxisShaper::Configure(GCodeBuffer& gb, const StringRef& reply) THROW
if (gb.Seen('L'))
{
seen = true;
- minimumAcceleration = ConvertAcceleration(max<float>(gb.GetFValue(), 1.0)); // very low accelerations cause problems with the maths
+ minimumAcceleration = ConvertAcceleration(max<float>(gb.GetNonNegativeFValue(), 1.0)); // very low accelerations cause problems with the maths
}
if (gb.Seen('S'))
{
diff --git a/src/Movement/AxisShaper.h b/src/Movement/AxisShaper.h
index d4740a5f..be1d95c4 100644
--- a/src/Movement/AxisShaper.h
+++ b/src/Movement/AxisShaper.h
@@ -49,9 +49,7 @@ public:
static MoveSegment *GetUnshapedSegments(DDA& dda, const PrepParams& params) noexcept;
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(amplitudes)
- OBJECT_MODEL_ARRAY(durations)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
MoveSegment *GetAccelerationSegments(const DDA& dda, PrepParams& params) const noexcept;
diff --git a/src/Movement/BedProbing/Grid.cpp b/src/Movement/BedProbing/Grid.cpp
index 81211fa7..b0d42899 100644
--- a/src/Movement/BedProbing/Grid.cpp
+++ b/src/Movement/BedProbing/Grid.cpp
@@ -24,43 +24,45 @@
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(GridDefinition, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(...) OBJECT_MODEL_FUNC_IF_BODY(GridDefinition, __VA_ARGS__)
-static constexpr ObjectModelArrayDescriptor axisArrayDescriptor =
+constexpr ObjectModelArrayTableEntry GridDefinition::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetAxisLetter(context.GetLastIndex())); }
-};
-
-static constexpr ObjectModelArrayDescriptor maxsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetMax(context.GetLastIndex()), 1); }
-};
-
-static constexpr ObjectModelArrayDescriptor minsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetMin(context.GetLastIndex()), 1); }
+ // 0. Axes
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetAxisLetter(context.GetLastIndex())); }
+ },
+ // 1. Maximums
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetMax(context.GetLastIndex()), 1); }
+ },
+ // 2. Minimums
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetMin(context.GetLastIndex()), 1); }
+ },
+ // 3. Spacings
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetSpacing(context.GetLastIndex()), 1); }
+ }
};
-static constexpr ObjectModelArrayDescriptor spacingsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const GridDefinition*)self)->GetSpacing(context.GetLastIndex()), 1); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(GridDefinition)
constexpr ObjectModelTableEntry GridDefinition::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. GridDefinition members
- { "axes", OBJECT_MODEL_FUNC_NOSELF(&axisArrayDescriptor), ObjectModelEntryFlags::none },
- { "maxs", OBJECT_MODEL_FUNC_NOSELF(&maxsArrayDescriptor), ObjectModelEntryFlags::none },
- { "mins", OBJECT_MODEL_FUNC_NOSELF(&minsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "axes", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
+ { "maxs", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
+ { "mins", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::none },
{ "radius", OBJECT_MODEL_FUNC(self->radius, 1), ObjectModelEntryFlags::none },
- { "spacings", OBJECT_MODEL_FUNC_NOSELF(&spacingsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "spacings", OBJECT_MODEL_FUNC_ARRAY(3), ObjectModelEntryFlags::none },
{ "xMax", OBJECT_MODEL_FUNC(self->maxs[0], 1), ObjectModelEntryFlags::obsolete },
{ "xMin", OBJECT_MODEL_FUNC(self->mins[0], 1), ObjectModelEntryFlags::obsolete },
@@ -76,7 +78,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE(GridDefinition)
#endif
-const char * const GridDefinition::HeightMapLabelLines[] =
+constexpr const char* GridDefinition::HeightMapLabelLines[] =
{
"xmin,xmax,ymin,ymax,radius,spacing,xnum,ynum", // old version label line
"xmin,xmax,ymin,ymax,radius,xspacing,yspacing,xnum,ynum", // label line until 3.3-beta1
@@ -323,10 +325,6 @@ void GridDefinition::PrintError(float originalAxis0range, float originalAxis1ran
}
}
-// Increase the version number in the following string whenever we change the format of the height map file significantly.
-// Adding more fields to the header row can be handled in GridDefinition::ReadParameters(), though.
-const char * const HeightMap::HeightMapComment = "RepRapFirmware height map file v2";
-
HeightMap::HeightMap() noexcept : useMap(false) { }
void HeightMap::SetGrid(const GridDefinition& gd) noexcept
@@ -439,7 +437,11 @@ bool HeightMap::SaveToFile(FileStore *f, const char *fname, float zOffset) noexc
}
// Load the grid from file, returning true if an error occurred with the error reason appended to the buffer
-bool HeightMap::LoadFromFile(FileStore *f, const char *fname, const StringRef& r) noexcept
+bool HeightMap::LoadFromFile(FileStore *f, const char *fname, const StringRef& r
+# if SUPPORT_PROBE_POINTS_FILE
+ , bool isPointsFile
+# endif
+ ) noexcept
{
const size_t MaxLineLength = (MaxAxis0GridPoints * 8) + 2; // maximum length of a line in the height map file, need 8 characters per grid point
const char* const readFailureText = "failed to read line from file";
@@ -450,15 +452,21 @@ bool HeightMap::LoadFromFile(FileStore *f, const char *fname, const StringRef& r
GridDefinition newGrid;
int gridVersion;
- if (f->ReadLine(buffer, sizeof(buffer)) <= 0)
+ if (f->ReadLine(buffer, sizeof(buffer)) <= 0) // read the header comment line, which includes the version
{
r.cat(readFailureText);
}
- else if (!StringStartsWith(buffer, HeightMapComment)) // check the version line is as expected
+ else if (!StringStartsWith(buffer, // check the version line is as expected
+# if SUPPORT_PROBE_POINTS_FILE
+ (isPointsFile) ? PointsFileComment :
+# endif
+ HeightMapComment
+ )
+ )
{
r.cat("bad header line or wrong version header");
}
- else if (f->ReadLine(buffer, sizeof(buffer)) <= 0)
+ else if (f->ReadLine(buffer, sizeof(buffer)) <= 0) // read and discard the column names line
{
r.cat(readFailureText);
}
@@ -495,7 +503,12 @@ bool HeightMap::LoadFromFile(FileStore *f, const char *fname, const StringRef& r
{
++p;
}
- if (*p == '0' && (p[1] == ',' || p[1] == 0))
+ if (
+#if SUPPORT_PROBE_POINTS_FILE
+ !isPointsFile &&
+#endif
+ (*p == '0' && (p[1] == ',' || p[1] == 0))
+ )
{
// Values of 0 with no decimal places in un-probed values, so leave the point set as not valid
++p;
@@ -509,7 +522,23 @@ bool HeightMap::LoadFromFile(FileStore *f, const char *fname, const StringRef& r
r.catf("number expected at line %" PRIu32 " column %d", row + 3, (p - buffer) + 1);
return true; // failed to read a number
}
- SetGridHeight(col, row, f);
+#if SUPPORT_PROBE_POINTS_FILE
+ if (isPointsFile)
+ {
+ if (f > 0.0)
+ {
+ gridPointInvalid.ClearBit((row * def.nums[0]) + col);
+ }
+ else
+ {
+ gridPointInvalid.SetBit((row * def.nums[0]) + col);
+ }
+ }
+ else
+#endif
+ {
+ SetGridHeight(col, row, f);
+ }
p = np;
}
if (*p == ',')
@@ -518,14 +547,19 @@ bool HeightMap::LoadFromFile(FileStore *f, const char *fname, const StringRef& r
}
}
}
- ExtrapolateMissing();
- fileName.copy(fname);
+#if SUPPORT_PROBE_POINTS_FILE
+ if (!isPointsFile)
+#endif
+ {
+ ExtrapolateMissing();
+ fileName.copy(fname);
+ }
return false; // success!
}
return true; // an error occurred
}
-#endif
+#endif // HAS_MASS_STORAGE || HAS_SBC_INTERFACE
// Return number of points probed, mean and RMS deviation, min and max error
unsigned int HeightMap::GetStatistics(Deviation& deviation, float& minError, float& maxError) const noexcept
@@ -565,6 +599,20 @@ bool HeightMap::UseHeightMap(bool b) noexcept
return useMap;
}
+// Return true if we can probe this point
+bool HeightMap::CanProbePoint(size_t axis0Index, size_t axis1Index) const noexcept
+{
+#if SUPPORT_PROBE_POINTS_FILE
+ if (gridPointInvalid.IsBitSet((axis1Index * def.nums[0]) + axis0Index))
+ {
+ return false;
+ }
+#endif
+ const float axis0Coord = def.GetCoordinate(0, axis0Index);
+ const float axis1Coord = def.GetCoordinate(1, axis1Index);
+ return def.IsInRadius(axis0Coord, axis1Coord);
+}
+
// Compute the height error at the specified point
float HeightMap::GetInterpolatedHeightError(float axis0, float axis1) const noexcept
{
@@ -595,7 +643,7 @@ float HeightMap::GetInterpolatedHeightError(float axis0, float axis1) const noex
return InterpolateAxis0Axis1(xIndex, yIndex, xf - xFloor, yf - yFloor);
}
-float HeightMap::InterpolateAxis0Axis1(uint32_t axis0Index, uint32_t axis1Index, float axis0Frac, float axis1Frac) const noexcept
+float HeightMap::InterpolateAxis0Axis1(size_t axis0Index, size_t axis1Index, float axis0Frac, float axis1Frac) const noexcept
{
const uint32_t indexX0Y0 = GetMapIndex(axis0Index, axis1Index); // (X0,Y0)
const uint32_t indexX1Y0 = indexX0Y0 + 1; // (X1,Y0)
@@ -684,20 +732,88 @@ void HeightMap::ExtrapolateMissing() noexcept
const float d = centAxis0*a + centAxis1*b + centZ*c;
// Fill in the blanks
- for (uint32_t iAxis1 = 0; iAxis1 < def.nums[1]; iAxis1++)
+ for (size_t iAxis1 = 0; iAxis1 < def.nums[1]; iAxis1++)
{
- for (uint32_t iAxis0 = 0; iAxis0 < def.nums[0]; iAxis0++)
+ for (size_t iAxis0 = 0; iAxis0 < def.nums[0]; iAxis0++)
{
const uint32_t index = GetMapIndex(iAxis0, iAxis1);
if (!gridHeightSet.IsBitSet(index))
{
- const float fAxis0 = (def.spacings[0] * iAxis0) + def.mins[0];
- const float fAxis1 = (def.spacings[1] * iAxis1) + def.mins[1];
- const float fZ = (d - (a * fAxis0 + b * fAxis1)) * invC;
- gridHeights[index] = fZ; // fill in Z but don't mark it as set so we can always differentiate between measured and extrapolated
+#if SUPPORT_PROBE_POINTS_FILE
+ // The point may be surrounded by points we have probed, in which case we need to interpolate instead of extrapolate
+ if (!InterpolateMissingPoint(iAxis0, iAxis1, gridHeights[index]))
+#endif
+ {
+ const float fAxis0 = (def.spacings[0] * iAxis0) + def.mins[0];
+ const float fAxis1 = (def.spacings[1] * iAxis1) + def.mins[1];
+ const float fZ = (d - (a * fAxis0 + b * fAxis1)) * invC;
+ gridHeights[index] = fZ; // fill in Z but don't mark it as set so we can always differentiate between measured and extrapolated
+ }
+ }
+ }
+ }
+}
+
+#if SUPPORT_PROBE_POINTS_FILE
+
+// Try to fill in a grid height by interpolation, if it is surrounded by a sufficient number of probed points
+// Return true if success with 'height' set to the interpolated height, else false
+bool HeightMap::InterpolateMissingPoint(size_t axis0Index, size_t axis1Index, float& height) const noexcept
+{
+ float total = 0;
+ unsigned int numPointsUsed = 0;
+
+ // Try the 2 points either side
+ if ( axis0Index != 0
+ && axis0Index + 1 < def.nums[0]
+ && gridHeightSet.IsBitSet(GetMapIndex(axis0Index - 1, axis1Index))
+ && gridHeightSet.IsBitSet(GetMapIndex(axis0Index + 1, axis1Index))
+ )
+ {
+ total += gridHeights[GetMapIndex(axis0Index - 1, axis1Index)] + gridHeights[GetMapIndex(axis0Index + 1, axis1Index)];
+ numPointsUsed += 2;
+ }
+
+ // Try the 2 points above and below
+ if ( axis1Index != 0
+ && axis1Index + 1 < def.nums[1]
+ )
+ {
+ if ( gridHeightSet.IsBitSet(GetMapIndex(axis0Index, axis1Index - 1))
+ && gridHeightSet.IsBitSet(GetMapIndex(axis0Index, axis1Index + 1))
+ )
+ {
+ total += gridHeights[GetMapIndex(axis0Index, axis1Index - 1)] + gridHeights[GetMapIndex(axis0Index, axis1Index + 1)];
+ numPointsUsed += 2;
+ }
+
+ // If we found 0 or 1 pairs of points to interpolate between, also try both sets of diagonal points
+ if ( numPointsUsed < 4
+ && axis0Index != 0
+ && axis0Index + 1 < def.nums[0]
+ )
+ {
+ if (gridHeightSet.IsBitSet(GetMapIndex(axis0Index - 1, axis1Index - 1)) && gridHeightSet.IsBitSet(GetMapIndex(axis0Index + 1, axis1Index + 1)))
+ {
+ total += gridHeights[GetMapIndex(axis0Index - 1, axis1Index - 1)] + gridHeights[GetMapIndex(axis0Index + 1, axis1Index + 1)];
+ numPointsUsed += 2;
+ }
+ if (gridHeightSet.IsBitSet(GetMapIndex(axis0Index - 1, axis1Index + 1)) && gridHeightSet.IsBitSet(GetMapIndex(axis0Index + 1, axis1Index - 1)))
+ {
+ total += gridHeights[GetMapIndex(axis0Index - 1, axis1Index + 1)] + gridHeights[GetMapIndex(axis0Index + 1, axis1Index - 1)];
+ numPointsUsed += 2;
}
}
}
+
+ if (numPointsUsed != 0)
+ {
+ height = total/numPointsUsed;
+ return true;
+ }
+ return false;
}
+#endif
+
// End
diff --git a/src/Movement/BedProbing/Grid.h b/src/Movement/BedProbing/Grid.h
index cd8bd857..a4137e06 100644
--- a/src/Movement/BedProbing/Grid.h
+++ b/src/Movement/BedProbing/Grid.h
@@ -32,7 +32,6 @@ public:
uint32_t NumAxisPoints(size_t axis) const noexcept pre(axis < 2) { return nums[axis]; }
uint32_t NumPoints() const noexcept { return nums[0] * nums[1]; }
float GetCoordinate(size_t axis, size_t coordinateIndex) const noexcept pre(axis < 2) { return mins[axis] + (coordinateIndex * spacings[axis]); }
- bool IsInRadius(float x, float y) const noexcept;
bool IsValid() const noexcept { return isValid; }
bool Set(const char axisLetters[2], const float axis0Range[2], const float axis1Range[2], float pRadius, const float pSpacings[2]) noexcept;
@@ -45,9 +44,10 @@ public:
pre(!IsValid());
protected:
- DECLARE_OBJECT_MODEL
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
+ bool IsInRadius(float x, float y) const noexcept;
void CheckValidity(bool setNum0Num1) noexcept;
static constexpr float MinSpacing = 0.1; // The minimum point spacing allowed
@@ -81,9 +81,14 @@ public:
void SetGridHeight(size_t axis0Index, size_t axis1Index, float height) noexcept; // Set the height of a grid point
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
- bool SaveToFile(FileStore *f, const char *fname, float zOffset) noexcept // Save the grid to file returning true if an error occurred
- pre(IsValid());
- bool LoadFromFile(FileStore *f, const char *fname, const StringRef& r) noexcept; // Load the grid from file returning true if an error occurred
+ bool SaveToFile(FileStore *f, const char *fname, float zOffset) noexcept // Save the grid to file returning true if an error occurred
+ pre(IsValid());
+
+ bool LoadFromFile(FileStore *f, const char *fname, const StringRef& r
+# if SUPPORT_PROBE_POINTS_FILE
+ , bool isPointsFile
+# endif
+ ) noexcept; // Load the grid from file returning true if an error occurred
const char *GetFileName() const noexcept { return fileName.c_str(); }
#endif
@@ -93,12 +98,23 @@ public:
bool UseHeightMap(bool b) noexcept;
bool UsingHeightMap() const noexcept { return useMap; }
- unsigned int GetStatistics(Deviation& deviation, float& minError, float& maxError) const noexcept;
- // Return number of points probed, mean and RMS deviation, min and max error
- void ExtrapolateMissing() noexcept; // Extrapolate missing points to ensure consistency
+ unsigned int GetStatistics(Deviation& deviation, float& minError, float& maxError) const noexcept; // Return number of points probed, mean and RMS deviation, min and max error
+ bool CanProbePoint(size_t axis0Index, size_t axis1Index) const noexcept; // Return true if we can probe this point
+ void ExtrapolateMissing() noexcept; // Extrapolate missing points to ensure consistency
+
+#if SUPPORT_PROBE_POINTS_FILE
+ void ClearProbePointsInvalid() noexcept { gridPointInvalid.ClearAll(); }
+#endif
private:
- static const char * const HeightMapComment; // The start of the comment we write at the start of the height map file
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
+ // Increase the version number in the following string whenever we change the format of the height map file significantly.
+ // Adding more fields to the header row can be handled in GridDefinition::ReadParameters() though.
+ static constexpr const char* HeightMapComment = "RepRapFirmware height map file v2"; // The start of the comment we write at the start of the height map file
+# if SUPPORT_PROBE_POINTS_FILE
+ static constexpr const char* PointsFileComment = "RepRapFirmware probe points file v2"; // The start of the comment we write at the start of the points map file
+# endif
+#endif
GridDefinition def;
float gridHeights[MaxGridProbePoints]; // The Z coordinates of the points on the bed that were probed
@@ -106,12 +122,19 @@ private:
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
String<MaxFilenameLength> fileName; // The name of the file that this height map was loaded from or saved to
#endif
+#if SUPPORT_PROBE_POINTS_FILE
+ LargeBitmap<MaxGridProbePoints> gridPointInvalid; // Bitmap of which points are not valid
+#endif
bool useMap; // True to do bed compensation
- uint32_t GetMapIndex(uint32_t axis0Index, uint32_t axis1Index) const noexcept { return (axis1Index * def.NumAxisPoints(0)) + axis0Index; }
+ size_t GetMapIndex(size_t axis0Index, size_t axis1Index) const noexcept { return (axis1Index * def.NumAxisPoints(0)) + axis0Index; }
void SetGridHeight(size_t index, float height) noexcept; // Set the height of a grid point
- float InterpolateAxis0Axis1(uint32_t axis0Index, uint32_t axis1Index, float axis0Frac, float axis1Frac) const noexcept;
+ float InterpolateAxis0Axis1(size_t axis0Index, size_t axis1Index, float axis0Frac, float axis1Frac) const noexcept;
+
+#if SUPPORT_PROBE_POINTS_FILE
+ bool InterpolateMissingPoint(size_t axis0Index, size_t axis1Index, float& height) const noexcept;
+#endif
};
#endif /* SRC_MOVEMENT_GRID_H_ */
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index 751841c0..2621020e 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -352,10 +352,10 @@ bool DDA::InitStandardMove(DDARing& ring, const RawMove &nextMove, bool doMotorM
else if (nextMove.linearAxesMentioned)
{
linearAxesMoving = true;
- if (Tool::GetXAxes(nextMove.tool).IsBitSet(drive) || Tool::GetYAxes(nextMove.tool).IsBitSet(drive))
- {
- flags.xyMoving = true; // this move has XY movement in user space, before axis were mapped
- }
+ }
+ if (Tool::GetXAxes(nextMove.movementTool).IsBitSet(drive) || Tool::GetYAxes(nextMove.movementTool).IsBitSet(drive))
+ {
+ flags.xyMoving = true; // this move has XY movement in user space, before axis were mapped
}
}
}
@@ -430,10 +430,10 @@ bool DDA::InitStandardMove(DDARing& ring, const RawMove &nextMove, bool doMotorM
}
// 3. Store some values
- tool = nextMove.tool;
+ tool = nextMove.movementTool;
flags.checkEndstops = nextMove.checkEndstops;
filePos = nextMove.filePos;
- virtualExtruderPosition = nextMove.virtualExtruderPosition;
+ virtualExtruderPosition = nextMove.moveStartVirtualExtruderPosition;
proportionDone = nextMove.proportionDone;
initialUserC0 = nextMove.initialUserC0;
initialUserC1 = nextMove.initialUserC1;
@@ -496,15 +496,15 @@ bool DDA::InitStandardMove(DDARing& ring, const RawMove &nextMove, bool doMotorM
memcpyf(normalisedDirectionVector, directionVector, ARRAY_SIZE(normalisedDirectionVector));
Absolute(normalisedDirectionVector, MaxAxesPlusExtruders);
acceleration = beforePrepare.maxAcceleration = VectorBoxIntersection(normalisedDirectionVector, accelerations);
- if (flags.xyMoving) // apply M204 acceleration limits to XY moves
+ if (flags.xyMoving) // apply M204 acceleration limits to XY moves
{
- acceleration = min<float>(acceleration, (flags.isPrintingMove) ? move.GetMaxPrintingAcceleration() : move.GetMaxTravelAcceleration());
+ acceleration = min<float>(acceleration, (flags.isPrintingMove) ? nextMove.maxPrintingAcceleration : nextMove.maxTravelAcceleration);
}
deceleration = acceleration;
// 6. Set the speed to the smaller of the requested and maximum speed.
// Also enforce a minimum speed of 0.5mm/sec. We need a minimum speed to avoid overflow in the movement calculations.
- float reqSpeed = nextMove.feedRate;
+ float reqSpeed = (nextMove.inverseTimeMode) ? totalDistance/nextMove.feedRate : nextMove.feedRate;
if (!doMotorMapping)
{
// Special case of a raw or homing move on a delta printer
@@ -1939,7 +1939,7 @@ pre(state == frozen)
if ((extrusions | retractions) != 0)
{
// Check for trying to extrude or retract when the hot end temperature is too low
- const unsigned int prohibitedMovements = reprap.GetProhibitedExtruderMovements(extrusions, retractions);
+ const unsigned int prohibitedMovements = Tool::GetProhibitedExtruderMovements(extrusions, retractions, tool);
for (DriveMovement **dmpp = &activeDMs; *dmpp != nullptr; )
{
DriveMovement* const dm = *dmpp;
@@ -2374,6 +2374,16 @@ void DDA::UpdateMovementAccumulators(volatile int32_t *accumulators) const noexc
#endif
}
+float DDA::GetTotalExtrusionRate() const noexcept
+{
+ float fraction = 0.0;
+ for (size_t i = MaxAxesPlusExtruders - reprap.GetGCodes().GetNumExtruders(); i < MaxAxesPlusExtruders; ++i)
+ {
+ fraction += directionVector[i];
+ }
+ return fraction * InverseConvertSpeedToMmPerSec(topSpeed);
+}
+
#if SUPPORT_LASER
// Manage the laser power. Return the number of ticks until we should be called again, or 0 to be called at the start of the next move.
diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h
index fe37aee3..d9d2143e 100644
--- a/src/Movement/DDA.h
+++ b/src/Movement/DDA.h
@@ -147,6 +147,8 @@ public:
float GetAccelerationMmPerSecSquared() const noexcept { return InverseConvertAcceleration(acceleration); }
float GetDecelerationMmPerSecSquared() const noexcept { return InverseConvertAcceleration(deceleration); }
float GetVirtualExtruderPosition() const noexcept { return virtualExtruderPosition; }
+ float GetTotalExtrusionRate() const noexcept;
+
float AdvanceBabyStepping(DDARing& ring, size_t axis, float amount) noexcept; // Try to push babystepping earlier in the move queue
const Tool *GetTool() const noexcept { return tool; }
float GetTotalDistance() const noexcept { return totalDistance; }
diff --git a/src/Movement/DDARing.cpp b/src/Movement/DDARing.cpp
index 9054705b..5297f924 100644
--- a/src/Movement/DDARing.cpp
+++ b/src/Movement/DDARing.cpp
@@ -644,6 +644,17 @@ void DDARing::GetCurrentMachinePosition(float m[MaxAxes], bool disableMotorMappi
}
}
+#if SUPPORT_ASYNC_MOVES
+
+// Return the machine coordinates of just some axes
+void DDARing::GetPartialMachinePosition(float m[MaxAxes], AxesBitmap whichAxes) const noexcept
+{
+ DDA * const lastQueuedMove = addPointer->GetPrevious();
+ whichAxes.Iterate([m, lastQueuedMove](unsigned int axis, unsigned int count) { m[axis] = lastQueuedMove->GetEndCoordinate(axis, false); });
+}
+
+#endif
+
// These are the actual numbers we want in the positions, so don't transform them.
void DDARing::SetPositions(const float move[MaxAxesPlusExtruders]) noexcept
{
@@ -769,6 +780,12 @@ float DDARing::GetDecelerationMmPerSecSquared() const noexcept
return (cdda != nullptr) ? cdda->GetDecelerationMmPerSecSquared() : 0.0;
}
+float DDARing::GetTotalExtrusionRate() const noexcept
+{
+ const DDA* const cdda = currentDda; // capture volatile variable
+ return (cdda != nullptr) ? cdda->GetTotalExtrusionRate() : 0.0;
+}
+
// Pause the print as soon as we can, returning true if we are able to skip any moves and updating 'rp' to the first move we skipped.
// Called from GCodes by the Main task
bool DDARing::PauseMoves(RestorePoint& rp) noexcept
@@ -962,12 +979,12 @@ bool DDARing::LowPowerOrStallPause(RestorePoint& rp) noexcept
#endif
-void DDARing::Diagnostics(MessageType mtype, const char *prefix) noexcept
+void DDARing::Diagnostics(MessageType mtype, unsigned int ringNumber) noexcept
{
const DDA * const cdda = currentDda;
reprap.GetPlatform().MessageF(mtype,
- "=== %sDDARing ===\nScheduled moves %" PRIu32 ", completed %" PRIu32 ", hiccups %" PRIu32 ", stepErrors %u, LaErrors %u, Underruns [%u, %u, %u], CDDA state %d\n",
- prefix, scheduledMoves, completedMoves, numHiccups, stepErrors, numLookaheadErrors, numLookaheadUnderruns, numPrepareUnderruns, numNoMoveUnderruns,
+ "=== DDARing %u ===\nScheduled moves %" PRIu32 ", completed %" PRIu32 ", hiccups %" PRIu32 ", stepErrors %u, LaErrors %u, Underruns [%u, %u, %u], CDDA state %d\n",
+ ringNumber, scheduledMoves, completedMoves, numHiccups, stepErrors, numLookaheadErrors, numLookaheadUnderruns, numPrepareUnderruns, numNoMoveUnderruns,
(cdda == nullptr) ? -1 : (int)cdda->GetState());
numHiccups = stepErrors = numLookaheadUnderruns = numPrepareUnderruns = numNoMoveUnderruns = numLookaheadErrors = 0;
}
diff --git a/src/Movement/DDARing.h b/src/Movement/DDARing.h
index e0c28685..ea15bf34 100644
--- a/src/Movement/DDARing.h
+++ b/src/Movement/DDARing.h
@@ -60,9 +60,14 @@ public:
float GetTopSpeedMmPerSec() const noexcept;
float GetAccelerationMmPerSecSquared() const noexcept;
float GetDecelerationMmPerSecSquared() const noexcept;
+ float GetTotalExtrusionRate() const noexcept;
int32_t GetEndPoint(size_t drive) const noexcept { return liveEndPoints[drive]; } // Get the current position of a motor
void GetCurrentMachinePosition(float m[MaxAxes], bool disableMotorMapping) const noexcept; // Get the current position in untransformed coords
+#if SUPPORT_ASYNC_MOVES
+ void GetPartialMachinePosition(float m[MaxAxes], AxesBitmap whichAxes) const noexcept; // Return the machine coordinates of just some axes
+#endif
+
void SetPositions(const float move[MaxAxesPlusExtruders]) noexcept; // Force the machine coordinates to be these
void AdjustMotorPositions(const float adjustment[], size_t numMotors) noexcept; // Perform motor endpoint adjustment
bool LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept; // Fetch the last point at the end of the last completed DDA if it has changed since we last called this
@@ -80,7 +85,7 @@ public:
#endif
void RecordLookaheadError() noexcept { ++numLookaheadErrors; } // Record a lookahead error
- void Diagnostics(MessageType mtype, const char *prefix) noexcept;
+ void Diagnostics(MessageType mtype, unsigned int ringNumber) noexcept;
bool SetWaitingToEmpty() noexcept;
diff --git a/src/Movement/HeightControl/HeightController.cpp b/src/Movement/HeightControl/HeightController.cpp
index d066d692..08f6424e 100644
--- a/src/Movement/HeightControl/HeightController.cpp
+++ b/src/Movement/HeightControl/HeightController.cpp
@@ -44,7 +44,7 @@ GCodeResult HeightController::Configure(GCodeBuffer& gb, const StringRef& reply)
gb.TryGetFValue('D', configuredPidD, seen);
if (gb.Seen('F'))
{
- const float freq = gb.GetFValue();
+ const float freq = gb.GetPositiveFValue();
if (freq >= 0.1 && freq <= 200.0)
{
sampleInterval = lrintf(1000/freq);
@@ -53,10 +53,7 @@ GCodeResult HeightController::Configure(GCodeBuffer& gb, const StringRef& reply)
float zLimits[2];
bool seenZ = false;
- if (gb.TryGetFloatArray('Z', 2, zLimits, reply, seenZ, false))
- {
- return GCodeResult::error;
- }
+ gb.TryGetFloatArray('Z', 2, zLimits, seenZ, false);
if (seenZ && zLimits[0] < zLimits[1])
{
zMin = zLimits[0];
@@ -89,7 +86,7 @@ GCodeResult HeightController::Configure(GCodeBuffer& gb, const StringRef& reply)
}
// Start/stop height following
-GCodeResult HeightController::StartHeightFollowing(GCodeBuffer& gb, const StringRef& reply) noexcept
+GCodeResult HeightController::StartHeightFollowing(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
if (gb.Seen('P'))
{
@@ -107,10 +104,7 @@ GCodeResult HeightController::StartHeightFollowing(GCodeBuffer& gb, const String
float zLimits[2];
bool seenZ = false;
- if (gb.TryGetFloatArray('Z', 2, zLimits, reply, seenZ, false))
- {
- return GCodeResult::error;
- }
+ gb.TryGetFloatArray('Z', 2, zLimits, seenZ, false);
if (seenZ && zLimits[0] < zLimits[1])
{
zMin = zLimits[0];
diff --git a/src/Movement/HeightControl/HeightController.h b/src/Movement/HeightControl/HeightController.h
index c9d243fb..6db3c032 100644
--- a/src/Movement/HeightControl/HeightController.h
+++ b/src/Movement/HeightControl/HeightController.h
@@ -20,7 +20,7 @@ public:
HeightController() noexcept;
GCodeResult Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException);
- GCodeResult StartHeightFollowing(GCodeBuffer& gb, const StringRef& reply) noexcept; // Start/stop height following
+ GCodeResult StartHeightFollowing(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Start/stop height following
void Stop() noexcept; // stop height following mode
[[noreturn]] void RunTask() noexcept;
diff --git a/src/Movement/Kinematics/CoreKinematics.cpp b/src/Movement/Kinematics/CoreKinematics.cpp
index 2bbf806d..2f8cac57 100644
--- a/src/Movement/Kinematics/CoreKinematics.cpp
+++ b/src/Movement/Kinematics/CoreKinematics.cpp
@@ -21,42 +21,44 @@
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(CoreKinematics, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor CoreKinematics::forwardMatrixElementArrayDescriptor =
+constexpr ObjectModelArrayTableEntry CoreKinematics::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const CoreKinematics*)self)->forwardMatrix(context.GetIndex(1), context.GetIndex(0)), 3); }
-};
-
-constexpr ObjectModelArrayDescriptor CoreKinematics::inverseMatrixElementArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const CoreKinematics*)self)->inverseMatrix(context.GetIndex(1), context.GetIndex(0)), 3); }
-};
-
-constexpr ObjectModelArrayDescriptor CoreKinematics::forwardMatrixArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&forwardMatrixElementArrayDescriptor); }
+ // 20. Forward matrix elements in a row
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const CoreKinematics*)self)->forwardMatrix(context.GetIndex(1), context.GetLastIndex()), 3); }
+ },
+ // 21. Inverse matrix elements in a row
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const CoreKinematics*)self)->inverseMatrix(context.GetIndex(1), context.GetLastIndex()), 3); }
+ },
+ // 22. Forward matrix rows
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 20 | (context.GetLastIndex() << 8), true); }
+ },
+ // 23. Inverse matrix rows
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 21 | (context.GetLastIndex() << 8), true); }
+ }
};
-constexpr ObjectModelArrayDescriptor CoreKinematics::inverseMatrixArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&inverseMatrixElementArrayDescriptor); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE_WITH_PARENT(CoreKinematics, ZLeadscrewKinematics, 20)
constexpr ObjectModelTableEntry CoreKinematics::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. kinematics members
- { "forwardMatrix", OBJECT_MODEL_FUNC_NOSELF(&forwardMatrixArrayDescriptor), ObjectModelEntryFlags::none },
- { "inverseMatrix", OBJECT_MODEL_FUNC_NOSELF(&inverseMatrixArrayDescriptor), ObjectModelEntryFlags::none },
+ { "forwardMatrix", OBJECT_MODEL_FUNC_ARRAY(22), ObjectModelEntryFlags::none },
+ { "inverseMatrix", OBJECT_MODEL_FUNC_ARRAY(23), ObjectModelEntryFlags::none },
{ "name", OBJECT_MODEL_FUNC(self->GetName(true)), ObjectModelEntryFlags::none },
};
diff --git a/src/Movement/Kinematics/CoreKinematics.h b/src/Movement/Kinematics/CoreKinematics.h
index 9f230825..035fce4f 100644
--- a/src/Movement/Kinematics/CoreKinematics.h
+++ b/src/Movement/Kinematics/CoreKinematics.h
@@ -29,11 +29,7 @@ public:
AxesBitmap GetLinearAxes() const noexcept override;
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(forwardMatrix)
- OBJECT_MODEL_ARRAY(forwardMatrixElement)
- OBJECT_MODEL_ARRAY(inverseMatrix)
- OBJECT_MODEL_ARRAY(inverseMatrixElement)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
void Recalc() noexcept; // recalculate internal variables following a configuration change
diff --git a/src/Movement/Kinematics/FiveBarScaraKinematics.cpp b/src/Movement/Kinematics/FiveBarScaraKinematics.cpp
index 7615e261..cf09d5e4 100644
--- a/src/Movement/Kinematics/FiveBarScaraKinematics.cpp
+++ b/src/Movement/Kinematics/FiveBarScaraKinematics.cpp
@@ -556,13 +556,13 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
// parameter X: x origins of actuators
float paraX[2];
- gb.TryGetFloatArray('X', 2, paraX, reply, seen);
+ gb.TryGetFloatArray('X', 2, paraX, seen);
xOrigL = paraX[0];
xOrigR = paraX[1];
// parameter Y: y origins of actuators
float paraY[2];
- gb.TryGetFloatArray('Y', 2, paraY, reply, seen);
+ gb.TryGetFloatArray('Y', 2, paraY, seen);
yOrigL = paraY[0];
yOrigR = paraY[1];
@@ -585,7 +585,7 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
// proximal arm lengths
float proximalLengths[2];
- gb.TryGetFloatArray('P', 2, proximalLengths, reply, seen);
+ gb.TryGetFloatArray('P', 2, proximalLengths, seen);
proximalL = proximalLengths[0];
proximalR = proximalLengths[1];
@@ -594,7 +594,8 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
float distalLengths[4];
//TODO TryGetFloatArray will report an error if the wrong number of values is provided. But we want to allow either 2 or 4.
//TODO So this code should call Seen() followed by GetFloatArray() instead, then check the number of returned values.
- if (!gb.TryGetFloatArray('D', 4, distalLengths, reply, dseen) && dseen)
+ gb.TryGetFloatArray('D', 4, distalLengths, dseen);
+ if (dseen)
{
distalL = distalLengths[0];
distalR = distalLengths[1];
@@ -605,7 +606,8 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
else
{
dseen = false;
- if (!gb.TryGetFloatArray('D', 2, distalLengths, reply, dseen) && dseen)
+ gb.TryGetFloatArray('D', 2, distalLengths, dseen);
+ if (dseen)
{
distalL = distalLengths[0];
distalR = distalLengths[1];
@@ -619,7 +621,7 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
if (gb.Seen('B'))
{
float homingAngles[2];
- gb.TryGetFloatArray('B', 2, homingAngles, reply, seen);
+ gb.TryGetFloatArray('B', 2, homingAngles, seen);
homingAngleL = homingAngles[0];
homingAngleR = homingAngles[1];
}
@@ -652,7 +654,7 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
if (gb.Seen('A'))
{
float angles[6];
- gb.TryGetFloatArray('A', 6, angles, reply, seen);
+ gb.TryGetFloatArray('A', 6, angles, seen);
headAngleMin = angles[0];
headAngleMax = angles[1];
proxDistLAngleMin = angles[2];
@@ -674,7 +676,7 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
if (gb.Seen('C'))
{
float angles[4];
- gb.TryGetFloatArray('C', 4, angles, reply, seen);
+ gb.TryGetFloatArray('C', 4, angles, seen);
actuatorAngleLMin = angles[0];
actuatorAngleLMax = angles[1];
actuatorAngleRMin = angles[2];
@@ -693,7 +695,7 @@ bool FiveBarScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, cons
if (gb.Seen('Z'))
{
float coordinates[4];
- gb.TryGetFloatArray('Z', 4, coordinates, reply, seenNonGeometry);
+ gb.TryGetFloatArray('Z', 4, coordinates, seenNonGeometry);
for (int i=0; i < 4; i++)
{
printArea[i] = coordinates[i];
diff --git a/src/Movement/Kinematics/HangprinterKinematics.cpp b/src/Movement/Kinematics/HangprinterKinematics.cpp
index 01fe5a75..48eafcda 100644
--- a/src/Movement/Kinematics/HangprinterKinematics.cpp
+++ b/src/Movement/Kinematics/HangprinterKinematics.cpp
@@ -17,8 +17,6 @@
#include <General/Portability.h>
-// Default anchor coordinates
-// These are only placeholders. Each machine must have these values calibrated in order to work correctly.
constexpr float DefaultAnchors[4][3] = {{ 0.0, -2000.0, -100.0},
{ 2000.0, 1000.0, -100.0},
{-2000.0, 1000.0, -100.0},
@@ -34,32 +32,36 @@ constexpr float DefaultPrintRadius = 1500.0;
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(HangprinterKinematics, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor HangprinterKinematics::anchorCoordinatesArrayDescriptor =
+constexpr ObjectModelArrayTableEntry HangprinterKinematics::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 3; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const HangprinterKinematics *)self)->anchors[context.GetIndex(1)][context.GetLastIndex()], 1); }
+ // 10. Coordinates of one anchor
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 3; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const HangprinterKinematics *)self)->anchors[context.GetIndex(1)][context.GetLastIndex()], 1); }
+ },
+ // 11. Anchors
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return HANGPRINTER_AXES; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 10 | (context.GetLastIndex() << 8), true); }
+ }
};
-constexpr ObjectModelArrayDescriptor HangprinterKinematics::anchorsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return HANGPRINTER_AXES; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&anchorCoordinatesArrayDescriptor); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE_WITH_PARENT(HangprinterKinematics, RoundBedKinematics, 10)
constexpr ObjectModelTableEntry HangprinterKinematics::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. kinematics members
- { "anchors", OBJECT_MODEL_FUNC_NOSELF(&anchorsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "anchors", OBJECT_MODEL_FUNC_ARRAY(11), ObjectModelEntryFlags::none },
{ "name", OBJECT_MODEL_FUNC(self->GetName(true)), ObjectModelEntryFlags::none },
{ "printRadius", OBJECT_MODEL_FUNC(self->printRadius, 1), ObjectModelEntryFlags::none },
};
constexpr uint8_t HangprinterKinematics::objectModelTableDescriptor[] = { 1, 3 };
-DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(HangprinterKinematics, Kinematics)
+DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(HangprinterKinematics, RoundBedKinematics)
#endif
@@ -88,6 +90,14 @@ void HangprinterKinematics::Init() noexcept
constexpr uint32_t DefaultMotorGearTeeth[4] = { 20, 20, 20, 20}; // HP4 default
constexpr uint32_t DefaultSpoolGearTeeth[4] = { 255, 255, 255, 255}; // HP4 default
constexpr uint32_t DefaultFullStepsPerMotorRev[4] = { 25, 25, 25, 25};
+ constexpr float DefaultMoverWeight_kg = 0.0F; // Zero disables flex compensation feature.
+ constexpr float DefaultSpringKPerUnitLength = 20000.0F; // Garda 1.1 is somewhere in the range [20000, 100000]
+ constexpr float DefaultMinPlannedForce_Newton[4] = { 0.0F };
+ constexpr float DefaultMaxPlannedForce_Newton[4] = { 70.0F, 70.0F, 70.0F, 70.0F };
+ constexpr float DefaultGuyWireLengths[HANGPRINTER_AXES] = { -1.0F }; // If one of these are negative they will be calculated in Recalc() instead
+ constexpr float DefaultTargetForce_Newton = 20.0F; // 20 chosen quite arbitrarily
+ constexpr float DefaultTorqueConstants[HANGPRINTER_AXES] = { 0.0F };
+
ARRAY_INIT(anchors, DefaultAnchors);
printRadius = DefaultPrintRadius;
spoolBuildupFactor = DefaultSpoolBuildupFactor;
@@ -97,9 +107,21 @@ void HangprinterKinematics::Init() noexcept
ARRAY_INIT(motorGearTeeth, DefaultMotorGearTeeth);
ARRAY_INIT(spoolGearTeeth, DefaultSpoolGearTeeth);
ARRAY_INIT(fullStepsPerMotorRev, DefaultFullStepsPerMotorRev);
+ moverWeight_kg = DefaultMoverWeight_kg;
+ springKPerUnitLength = DefaultSpringKPerUnitLength;
+ ARRAY_INIT(minPlannedForce_Newton, DefaultMinPlannedForce_Newton);
+ ARRAY_INIT(maxPlannedForce_Newton, DefaultMaxPlannedForce_Newton);
+ ARRAY_INIT(guyWireLengths, DefaultGuyWireLengths);
+ targetForce_Newton = DefaultTargetForce_Newton;
+ ARRAY_INIT(torqueConstants, DefaultTorqueConstants);
+
Recalc();
}
+static inline float hyp3(float const a[3], float const b[3]) {
+ return fastSqrtf(fsquare(a[2] - b[2]) + fsquare(a[1] - b[1]) + fsquare(a[0] - b[0]));
+}
+
// Recalculate the derived parameters
void HangprinterKinematics::Recalc() noexcept
{
@@ -107,13 +129,13 @@ void HangprinterKinematics::Recalc() noexcept
// This is the difference between a "line length" and a "line position"
// "line length" == ("line position" + "line length in origin")
- lineLengthsOrigin[A_AXIS] = fastSqrtf(fsquare(anchors[A_AXIS][0]) + fsquare(anchors[A_AXIS][1]) + fsquare(anchors[A_AXIS][2]));
- lineLengthsOrigin[B_AXIS] = fastSqrtf(fsquare(anchors[B_AXIS][0]) + fsquare(anchors[B_AXIS][1]) + fsquare(anchors[B_AXIS][2]));
- lineLengthsOrigin[C_AXIS] = fastSqrtf(fsquare(anchors[C_AXIS][0]) + fsquare(anchors[C_AXIS][1]) + fsquare(anchors[C_AXIS][2]));
- lineLengthsOrigin[D_AXIS] = fastSqrtf(fsquare(anchors[D_AXIS][0]) + fsquare(anchors[D_AXIS][1]) + fsquare(anchors[D_AXIS][2]));
+ distancesOrigin[A_AXIS] = fastSqrtf(fsquare(anchors[A_AXIS][0]) + fsquare(anchors[A_AXIS][1]) + fsquare(anchors[A_AXIS][2]));
+ distancesOrigin[B_AXIS] = fastSqrtf(fsquare(anchors[B_AXIS][0]) + fsquare(anchors[B_AXIS][1]) + fsquare(anchors[B_AXIS][2]));
+ distancesOrigin[C_AXIS] = fastSqrtf(fsquare(anchors[C_AXIS][0]) + fsquare(anchors[C_AXIS][1]) + fsquare(anchors[C_AXIS][2]));
+ distancesOrigin[D_AXIS] = fastSqrtf(fsquare(anchors[D_AXIS][0]) + fsquare(anchors[D_AXIS][1]) + fsquare(anchors[D_AXIS][2]));
- // Line buildup compensation
+ //// Line buildup compensation
float stepsPerUnitTimesRTmp[HANGPRINTER_AXES] = { 0.0 };
Platform& platform = reprap.GetPlatform(); // No const because we want to set drive steper per unit
for (size_t i = 0; i < HANGPRINTER_AXES; i++)
@@ -136,6 +158,35 @@ void HangprinterKinematics::Recalc() noexcept
// Calculate the steps per unit that is correct at the origin
platform.SetDriveStepsPerUnit(i, stepsPerUnitTimesRTmp[i] / spoolRadii[i], 0);
}
+
+ //// Flex compensation
+
+ // If no guy wire lengths are configured, assume a default configuration
+ // with all spools stationary located at the D anchor
+ if (guyWireLengths[A_AXIS] < 0.0F or
+ guyWireLengths[B_AXIS] < 0.0F or
+ guyWireLengths[C_AXIS] < 0.0F or
+ guyWireLengths[D_AXIS] < 0.0F) {
+ guyWireLengths[A_AXIS] = hyp3(anchors[A_AXIS], anchors[D_AXIS]);
+ guyWireLengths[B_AXIS] = hyp3(anchors[B_AXIS], anchors[D_AXIS]);
+ guyWireLengths[C_AXIS] = hyp3(anchors[C_AXIS], anchors[D_AXIS]);
+ guyWireLengths[D_AXIS] = 0.0F;
+ }
+
+ for (size_t i{0}; i < HANGPRINTER_AXES; ++i) {
+ springKsOrigin[i] = SpringK(distancesOrigin[i] * mechanicalAdvantage[i] + guyWireLengths[i]);
+ }
+ float constexpr origin[3] = { 0.0F, 0.0F, 0.0F };
+ StaticForces(origin, fOrigin);
+ for (size_t i{0}; i < HANGPRINTER_AXES; ++i) {
+ relaxedSpringLengthsOrigin[i] = distancesOrigin[i] - fOrigin[i] / (springKsOrigin[i] * mechanicalAdvantage[i]);
+ }
+
+#if DUAL_CAN
+ // Setting and reading of forces.
+ ReadODrive3AxisForce({}, StringRef(nullptr, 0), torqueConstants, mechanicalAdvantage, spoolGearTeeth, motorGearTeeth, spoolRadii);
+ SetODrive3TorqueMode({}, 0.0F, StringRef(nullptr, 0), mechanicalAdvantage, spoolGearTeeth, motorGearTeeth, spoolRadii);
+#endif
}
// Return the name of the current kinematics
@@ -152,30 +203,14 @@ bool HangprinterKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
if (mCode == 669)
{
const bool seenNonGeometry = TryConfigureSegmentation(gb);
- if (gb.TryGetFloatArray('A', 3, anchors[A_AXIS], reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetFloatArray('B', 3, anchors[B_AXIS], reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetFloatArray('C', 3, anchors[C_AXIS], reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetFloatArray('D', 3, anchors[D_AXIS], reply, seen))
- {
- error = true;
- return true;
- }
+ gb.TryGetFloatArray('A', 3, anchors[A_AXIS], seen);
+ gb.TryGetFloatArray('B', 3, anchors[B_AXIS], seen);
+ gb.TryGetFloatArray('C', 3, anchors[C_AXIS], seen);
+ gb.TryGetFloatArray('D', 3, anchors[D_AXIS], seen);
if (gb.Seen('P'))
{
- printRadius = gb.GetFValue();
+ printRadius = gb.GetPositiveFValue();
seen = true;
}
@@ -203,36 +238,18 @@ bool HangprinterKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
else if (mCode == 666)
{
gb.TryGetFValue('Q', spoolBuildupFactor, seen);
- if (gb.TryGetFloatArray('R', HANGPRINTER_AXES, spoolRadii, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetUIArray('U', HANGPRINTER_AXES, mechanicalAdvantage, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetUIArray('O', HANGPRINTER_AXES, linesPerSpool, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetUIArray('L', HANGPRINTER_AXES, motorGearTeeth, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetUIArray('H', HANGPRINTER_AXES, spoolGearTeeth, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetUIArray('J', HANGPRINTER_AXES, fullStepsPerMotorRev, reply, seen))
- {
- error = true;
- return true;
- }
+ gb.TryGetFloatArray('R', HANGPRINTER_AXES, spoolRadii, seen);
+ gb.TryGetUIArray('U', HANGPRINTER_AXES, mechanicalAdvantage, seen);
+ gb.TryGetUIArray('O', HANGPRINTER_AXES, linesPerSpool, seen);
+ gb.TryGetUIArray('L', HANGPRINTER_AXES, motorGearTeeth, seen);
+ gb.TryGetUIArray('H', HANGPRINTER_AXES, spoolGearTeeth, seen);
+ gb.TryGetUIArray('J', HANGPRINTER_AXES, fullStepsPerMotorRev, seen);
+ gb.TryGetFValue('W', moverWeight_kg, seen);
+ gb.TryGetFValue('S', springKPerUnitLength, seen);
+ gb.TryGetFloatArray('I', HANGPRINTER_AXES, minPlannedForce_Newton, seen);
+ gb.TryGetFloatArray('X', HANGPRINTER_AXES, maxPlannedForce_Newton, seen);
+ gb.TryGetFloatArray('Y', HANGPRINTER_AXES, guyWireLengths, seen);
+ gb.TryGetFloatArray('C', HANGPRINTER_AXES, torqueConstants, seen);
if (seen)
{
Recalc();
@@ -240,20 +257,34 @@ bool HangprinterKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
else
{
reply.printf(
- "Q:Buildup fac %.4f\n"
- "R:Spool r %.2f, %.2f, %.2f, %.2f\n"
- "U:Mech Adv %d, %d, %d, %d\n"
- "O:Lines/spool %d, %d, %d, %d\n"
- "L:Motor gear teeth %d, %d, %d, %d\n"
- "H:Spool gear teeth %d, %d, %d, %d\n"
- "J:Full steps/rev %d, %d, %d, %d",
+ "M666 Q%.4f\n"
+ "R%.2f:%.2f:%.2f:%.2f\n"
+ "U%d:%d:%d:%d\n"
+ "O%d:%d:%d:%d\n"
+ "L%d:%d:%d:%d\n"
+ "H%d:%d:%d:%d\n"
+ "J%d:%d:%d:%d\n"
+ "W%.2f\n"
+ "S%.2f\n"
+ "I%.1f:%.1f:%.1f:%.1f\n"
+ "X%.1f:%.1f:%.1f:%.1f\n"
+ "Y%.1f:%.1f:%.1f:%.1f\n"
+ "T%.1f\n"
+ "C%.4f:%.4f:%.4f:%.4f",
(double)spoolBuildupFactor,
(double)spoolRadii[A_AXIS], (double)spoolRadii[B_AXIS], (double)spoolRadii[C_AXIS], (double)spoolRadii[D_AXIS],
(int)mechanicalAdvantage[A_AXIS], (int)mechanicalAdvantage[B_AXIS], (int)mechanicalAdvantage[C_AXIS], (int)mechanicalAdvantage[D_AXIS],
(int)linesPerSpool[A_AXIS], (int)linesPerSpool[B_AXIS], (int)linesPerSpool[C_AXIS], (int)linesPerSpool[D_AXIS],
(int)motorGearTeeth[A_AXIS], (int)motorGearTeeth[B_AXIS], (int)motorGearTeeth[C_AXIS], (int)motorGearTeeth[D_AXIS],
(int)spoolGearTeeth[A_AXIS], (int)spoolGearTeeth[B_AXIS], (int)spoolGearTeeth[C_AXIS], (int)spoolGearTeeth[D_AXIS],
- (int)fullStepsPerMotorRev[A_AXIS], (int)fullStepsPerMotorRev[B_AXIS], (int)fullStepsPerMotorRev[C_AXIS], (int)fullStepsPerMotorRev[D_AXIS]
+ (int)fullStepsPerMotorRev[A_AXIS], (int)fullStepsPerMotorRev[B_AXIS], (int)fullStepsPerMotorRev[C_AXIS], (int)fullStepsPerMotorRev[D_AXIS],
+ (double)moverWeight_kg,
+ (double)springKPerUnitLength,
+ (double)minPlannedForce_Newton[A_AXIS], (double)minPlannedForce_Newton[B_AXIS], (double)minPlannedForce_Newton[C_AXIS], (double)minPlannedForce_Newton[D_AXIS],
+ (double)maxPlannedForce_Newton[A_AXIS], (double)maxPlannedForce_Newton[B_AXIS], (double)maxPlannedForce_Newton[C_AXIS], (double)maxPlannedForce_Newton[D_AXIS],
+ (double)guyWireLengths[A_AXIS], (double)guyWireLengths[B_AXIS], (double)guyWireLengths[C_AXIS], (double)guyWireLengths[D_AXIS],
+ (double)targetForce_Newton,
+ (double)torqueConstants[A_AXIS], (double)torqueConstants[B_AXIS], (double)torqueConstants[C_AXIS], (double)torqueConstants[D_AXIS]
);
}
}
@@ -264,26 +295,34 @@ bool HangprinterKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
return seen;
}
-// Calculate the square of the line length from a spool from a Cartesian coordinate
-inline float HangprinterKinematics::LineLengthSquared(const float machinePos[3], const float anchors[3]) const noexcept
-{
- return fsquare(anchors[Z_AXIS] - machinePos[Z_AXIS]) + fsquare(anchors[Y_AXIS] - machinePos[Y_AXIS]) + fsquare(anchors[X_AXIS] - machinePos[X_AXIS]);
-}
-
// Convert Cartesian coordinates to motor coordinates, returning true if successful
bool HangprinterKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[],
size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[], bool isCoordinated) const noexcept
{
- float squaredLineLengths[HANGPRINTER_AXES];
- squaredLineLengths[A_AXIS] = LineLengthSquared(machinePos, anchors[A_AXIS]);
- squaredLineLengths[B_AXIS] = LineLengthSquared(machinePos, anchors[B_AXIS]);
- squaredLineLengths[C_AXIS] = LineLengthSquared(machinePos, anchors[C_AXIS]);
- squaredLineLengths[D_AXIS] = LineLengthSquared(machinePos, anchors[D_AXIS]);
+ float distances[HANGPRINTER_AXES];
+ distances[A_AXIS] = hyp3(machinePos, anchors[A_AXIS]);
+ distances[B_AXIS] = hyp3(machinePos, anchors[B_AXIS]);
+ distances[C_AXIS] = hyp3(machinePos, anchors[C_AXIS]);
+ distances[D_AXIS] = hyp3(machinePos, anchors[D_AXIS]);
+
+
+ float springKs[HANGPRINTER_AXES];
+ for (size_t i{0}; i < HANGPRINTER_AXES; ++i) {
+ springKs[i] = SpringK(distances[i] * mechanicalAdvantage[i] + guyWireLengths[i]);
+ }
+
+ float F[HANGPRINTER_AXES] = {0.0F}; // desired force in each direction
+ StaticForces(machinePos, F);
+
+ float relaxedSpringLengths[HANGPRINTER_AXES];
+ for (size_t i{0}; i < HANGPRINTER_AXES; ++i) {
+ relaxedSpringLengths[i] = distances[i] - F[i] / (springKs[i] * mechanicalAdvantage[i]);
+ // The second term there is the mover's movement in mm due to flex
+ }
float linePos[HANGPRINTER_AXES];
- for (size_t i = 0; i < HANGPRINTER_AXES; ++i)
- {
- linePos[i] = fastSqrtf(squaredLineLengths[i]) - lineLengthsOrigin[i];
+ for (size_t i = 0; i < HANGPRINTER_AXES; ++i) {
+ linePos[i] = relaxedSpringLengths[i] - relaxedSpringLengthsOrigin[i];
}
motorPos[A_AXIS] = lrintf(k0[A_AXIS] * (fastSqrtf(spoolRadiiSq[A_AXIS] + linePos[A_AXIS] * k2[A_AXIS]) - spoolRadii[A_AXIS]));
@@ -300,16 +339,66 @@ inline float HangprinterKinematics::MotorPosToLinePos(const int32_t motorPos, si
return (fsquare(motorPos / k0[axis] + spoolRadii[axis]) - spoolRadiiSq[axis]) / k2[axis];
}
+
+void HangprinterKinematics::flexDistances(float const machinePos[3], float const distanceA,
+ float const distanceB, float const distanceC,
+ float const distanceD, float flex[HANGPRINTER_AXES]) const noexcept {
+ float springKs[HANGPRINTER_AXES] = {
+ SpringK(distanceA * mechanicalAdvantage[A_AXIS] + guyWireLengths[A_AXIS]),
+ SpringK(distanceB * mechanicalAdvantage[B_AXIS] + guyWireLengths[B_AXIS]),
+ SpringK(distanceC * mechanicalAdvantage[C_AXIS] + guyWireLengths[C_AXIS]),
+ SpringK(distanceD * mechanicalAdvantage[D_AXIS] + guyWireLengths[D_AXIS])
+ };
+
+ float F[HANGPRINTER_AXES] = {0.0F}; // desired force in each direction
+ StaticForces(machinePos, F);
+
+ float relaxedSpringLengths[HANGPRINTER_AXES] = {
+ distanceA - F[A_AXIS] / (springKs[A_AXIS] * mechanicalAdvantage[A_AXIS]),
+ distanceB - F[B_AXIS] / (springKs[B_AXIS] * mechanicalAdvantage[B_AXIS]),
+ distanceC - F[C_AXIS] / (springKs[C_AXIS] * mechanicalAdvantage[C_AXIS]),
+ distanceD - F[D_AXIS] / (springKs[D_AXIS] * mechanicalAdvantage[D_AXIS])
+ };
+
+ float linePos[HANGPRINTER_AXES] = {
+ relaxedSpringLengths[A_AXIS] - relaxedSpringLengthsOrigin[A_AXIS],
+ relaxedSpringLengths[B_AXIS] - relaxedSpringLengthsOrigin[B_AXIS],
+ relaxedSpringLengths[C_AXIS] - relaxedSpringLengthsOrigin[C_AXIS],
+ relaxedSpringLengths[D_AXIS] - relaxedSpringLengthsOrigin[D_AXIS]
+ };
+
+ float distanceDifferences[HANGPRINTER_AXES] = {
+ distanceA - distancesOrigin[A_AXIS],
+ distanceB - distancesOrigin[B_AXIS],
+ distanceC - distancesOrigin[C_AXIS],
+ distanceD - distancesOrigin[D_AXIS]
+ };
+
+ flex[A_AXIS] = linePos[A_AXIS] - distanceDifferences[A_AXIS];
+ flex[B_AXIS] = linePos[B_AXIS] - distanceDifferences[B_AXIS];
+ flex[C_AXIS] = linePos[C_AXIS] - distanceDifferences[C_AXIS];
+ flex[D_AXIS] = linePos[D_AXIS] - distanceDifferences[D_AXIS];
+}
+
// Convert motor coordinates to machine coordinates.
// Assumes lines are tight and anchor location norms are followed
void HangprinterKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const noexcept
{
- ForwardTransform(
- MotorPosToLinePos(motorPos[A_AXIS], A_AXIS) + lineLengthsOrigin[A_AXIS],
- MotorPosToLinePos(motorPos[B_AXIS], B_AXIS) + lineLengthsOrigin[B_AXIS],
- MotorPosToLinePos(motorPos[C_AXIS], C_AXIS) + lineLengthsOrigin[C_AXIS],
- MotorPosToLinePos(motorPos[D_AXIS], D_AXIS) + lineLengthsOrigin[D_AXIS],
- machinePos);
+ float const distanceA = MotorPosToLinePos(motorPos[A_AXIS], A_AXIS) + distancesOrigin[A_AXIS];
+ float const distanceB = MotorPosToLinePos(motorPos[B_AXIS], B_AXIS) + distancesOrigin[B_AXIS];
+ float const distanceC = MotorPosToLinePos(motorPos[C_AXIS], C_AXIS) + distancesOrigin[C_AXIS];
+ float const distanceD = MotorPosToLinePos(motorPos[D_AXIS], D_AXIS) + distancesOrigin[D_AXIS];
+ ForwardTransform(distanceA, distanceB, distanceC, distanceD, machinePos);
+
+ // Now we have an approximate machinePos
+ // Let's correct for line flex
+ float flex[HANGPRINTER_AXES] = { 0.0F };
+ flexDistances(machinePos, distanceA, distanceB, distanceC, distanceD, flex);
+ float const adjustedDistanceA = distanceA - flex[A_AXIS];
+ float const adjustedDistanceB = distanceB - flex[B_AXIS];
+ float const adjustedDistanceC = distanceC - flex[C_AXIS];
+ float const adjustedDistanceD = distanceD - flex[D_AXIS];
+ ForwardTransform(adjustedDistanceA, adjustedDistanceB, adjustedDistanceC, adjustedDistanceD, machinePos);
}
static bool isSameSide(float const v0[3], float const v1[3], float const v2[3], float const v3[3], float const p[3]){
@@ -469,7 +558,7 @@ bool HangprinterKinematics::WriteCalibrationParameters(FileStore *f) const noexc
ok = f->Write(scratchString.c_str());
if (ok)
{
- scratchString.printf(" O%d:%d:%d:%d L%d:%d:%d:%d H%d:%d:%d:%d J%d:%d:%d:%d\n",
+ scratchString.printf(" O%d:%d:%d:%d L%d:%d:%d:%d H%d:%d:%d:%d J%d:%d:%d:%d",
(int)linesPerSpool[A_AXIS], (int)linesPerSpool[B_AXIS],
(int)linesPerSpool[C_AXIS], (int)linesPerSpool[D_AXIS],
(int)motorGearTeeth[A_AXIS], (int)motorGearTeeth[B_AXIS],
@@ -480,6 +569,28 @@ bool HangprinterKinematics::WriteCalibrationParameters(FileStore *f) const noexc
(int)fullStepsPerMotorRev[C_AXIS], (int)fullStepsPerMotorRev[D_AXIS]
);
ok = f->Write(scratchString.c_str());
+ if (ok)
+ {
+ scratchString.printf(" W%.2f S%.2f I%.1f:%.1f:%.1f:%.1f X%.1f:%.1f:%.1f:%.1f",
+ (double)moverWeight_kg, (double)springKPerUnitLength,
+ (double)minPlannedForce_Newton[A_AXIS], (double)minPlannedForce_Newton[B_AXIS],
+ (double)minPlannedForce_Newton[C_AXIS], (double)minPlannedForce_Newton[D_AXIS],
+ (double)maxPlannedForce_Newton[A_AXIS], (double)maxPlannedForce_Newton[B_AXIS],
+ (double)maxPlannedForce_Newton[C_AXIS], (double)maxPlannedForce_Newton[D_AXIS]
+ );
+ ok = f->Write(scratchString.c_str());
+ if (ok)
+ {
+ scratchString.printf(" Y%.1f:%.1f:%.1f:%.1f T%.1f C%.4f:%.4f:%.4f:%.4f\n",
+ (double)guyWireLengths[A_AXIS], (double)guyWireLengths[B_AXIS],
+ (double)guyWireLengths[C_AXIS], (double)guyWireLengths[D_AXIS],
+ (double)targetForce_Newton,
+ (double)torqueConstants[A_AXIS], (double)torqueConstants[B_AXIS],
+ (double)torqueConstants[C_AXIS], (double)torqueConstants[D_AXIS]
+ );
+ ok = f->Write(scratchString.c_str());
+ }
+ }
}
}
}
@@ -555,10 +666,10 @@ void HangprinterKinematics::ForwardTransform(float const a, float const b, float
}
}
- const float Asq = fsquare(lineLengthsOrigin[A_AXIS]);
- const float Bsq = fsquare(lineLengthsOrigin[B_AXIS]);
- const float Csq = fsquare(lineLengthsOrigin[C_AXIS]);
- const float Dsq = fsquare(lineLengthsOrigin[D_AXIS]);
+ const float Asq = fsquare(distancesOrigin[A_AXIS]);
+ const float Bsq = fsquare(distancesOrigin[B_AXIS]);
+ const float Csq = fsquare(distancesOrigin[C_AXIS]);
+ const float Dsq = fsquare(distancesOrigin[D_AXIS]);
const float aa = fsquare(a);
const float dd = fsquare(d);
const float k0b = (-fsquare(b) + Bsq - Dsq + dd) / (2.0 * anchors_tmp0[B_AXIS][X_AXIS]) + (anchors_tmp0[B_AXIS][Y_AXIS] / (2.0 * anchors_tmp0[A_AXIS][Y_AXIS] * anchors_tmp0[B_AXIS][X_AXIS])) * (Dsq - Asq + aa - dd);
@@ -594,6 +705,43 @@ void HangprinterKinematics::PrintParameters(const StringRef& reply) const noexce
}
#if DUAL_CAN
+HangprinterKinematics::ODriveAnswer HangprinterKinematics::GetODrive3MotorCurrent(DriverId driver, const StringRef& reply) THROWS(GCodeException)
+{
+ const uint8_t cmd = CANSimple::MSG_GET_IQ;
+ CanMessageBuffer * buf = CanInterface::ODrive::PrepareSimpleMessage(driver, reply);
+ if (buf == nullptr)
+ {
+ return {};
+ }
+ buf->id = CanInterface::ODrive::ArbitrationId(driver, cmd);
+ buf->remote = true; // Indicates that we expect an answer
+ CanInterface::ODrive::FlushCanReceiveHardware();
+ CanInterface::SendPlainMessageNoFree(buf);
+ bool ok = CanInterface::ODrive::GetExpectedSimpleMessage(buf, driver, cmd, reply);
+ float motorCurrent = 0.0;
+ if (ok)
+ {
+ size_t const expectedResponseLength = 8;
+ ok = (buf->dataLength == expectedResponseLength);
+ if (ok)
+ {
+ motorCurrent = LoadLEFloat(&(buf->msg.raw[4]));
+ }
+ else
+ {
+ reply.printf("Unexpected response length: %d", buf->dataLength);
+ }
+ }
+ CanMessageBuffer::Free(buf);
+ if (ok)
+ {
+ return {true, motorCurrent};
+ }
+ return {};
+}
+#endif // DUAL_CAN
+
+#if DUAL_CAN
HangprinterKinematics::ODriveAnswer HangprinterKinematics::GetODrive3EncoderEstimate(DriverId const driver, bool const makeReference, const StringRef& reply, bool const subtractReference) THROWS(GCodeException)
{
const uint8_t cmd = CANSimple::MSG_GET_ENCODER_ESTIMATES;
@@ -676,6 +824,52 @@ HangprinterKinematics::ODriveAnswer HangprinterKinematics::GetODrive3EncoderEsti
#endif // DUAL_CAN
#if DUAL_CAN
+GCodeResult HangprinterKinematics::ReadODrive3AxisForce(DriverId const driver, const StringRef& reply, float setTorqueConstants[], uint32_t setMechanicalAdvantage[], uint32_t setSpoolGearTeeth[], uint32_t setMotorGearTeeth[], float setSpoolRadii[]) THROWS(GCodeException)
+{
+ static float torqueConstants_[HANGPRINTER_AXES] = { 0.0 };
+ static uint32_t mechanicalAdvantage_[HANGPRINTER_AXES] = { 0 };
+ static uint32_t spoolGearTeeth_[HANGPRINTER_AXES] = { 0 };
+ static uint32_t motorGearTeeth_[HANGPRINTER_AXES] = { 0 };
+ static float spoolRadii_[HANGPRINTER_AXES] = { 0.0 };
+ if (setTorqueConstants != nullptr && setMechanicalAdvantage != nullptr && setSpoolGearTeeth != nullptr &&
+ setMotorGearTeeth != nullptr && setSpoolRadii != nullptr) {
+ for(size_t i{0}; i < HANGPRINTER_AXES; ++i) {
+ torqueConstants_[i] = setTorqueConstants[i];
+ mechanicalAdvantage_[i] = setMechanicalAdvantage[i];
+ spoolGearTeeth_[i] = setSpoolGearTeeth[i];
+ motorGearTeeth_[i] = setMotorGearTeeth[i];
+ spoolRadii_[i] = setSpoolRadii[i];
+ }
+ return GCodeResult::ok;
+ }
+
+ HangprinterKinematics::ODriveAnswer const motorCurrent = GetODrive3MotorCurrent(driver, reply);
+ if (motorCurrent.valid)
+ {
+ size_t const boardIndex = driver.boardAddress - 40;
+ if (boardIndex < 0 or boardIndex > 3) {
+ reply.catf("Board address not between 40 and 43: %d", driver.boardAddress);
+ return GCodeResult::error;
+ }
+ // This force calculation if very rough, assuming perfect data from ODrive,
+ // perfect transmission between motor gear and spool gear,
+ // the exact same line buildup on spool as we have at the origin,
+ // and no losses from any of the bearings or eyelets in the motion system.
+ float motorTorque_Nm = motorCurrent.value * torqueConstants_[boardIndex];
+ if (driver.boardAddress == 40 || driver.boardAddress == 41) // Driver direction is not stored on main board!! (will be in the future)
+ {
+ motorTorque_Nm = -motorTorque_Nm;
+ }
+ float const lineTension = 1000.0 * (motorTorque_Nm * (spoolGearTeeth_[boardIndex]/motorGearTeeth_[boardIndex])) / spoolRadii_[boardIndex];
+ float const force = lineTension * mechanicalAdvantage_[boardIndex];
+ reply.catf("%.2f, ", (double)(force));
+ return GCodeResult::ok;
+ }
+ return GCodeResult::error;
+}
+#endif // DUAL_CAN
+
+#if DUAL_CAN
GCodeResult HangprinterKinematics::ReadODrive3Encoder(DriverId const driver, GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
HangprinterKinematics::ODriveAnswer const estimate = GetODrive3EncoderEstimate(driver, gb.Seen('S'), reply, true);
@@ -694,7 +888,7 @@ GCodeResult HangprinterKinematics::ReadODrive3Encoder(DriverId const driver, GCo
#endif // DUAL_CAN
#if DUAL_CAN
-GCodeResult HangprinterKinematics::SetODrive3TorqueModeInner(DriverId const driver, float const torque, const StringRef& reply) noexcept
+GCodeResult HangprinterKinematics::SetODrive3TorqueModeInner(DriverId const driver, float const torque_Nm, const StringRef& reply) noexcept
{
// Get a buffer
CanMessageBuffer * buf = CanInterface::ODrive::PrepareSimpleMessage(driver, reply);
@@ -707,7 +901,7 @@ GCodeResult HangprinterKinematics::SetODrive3TorqueModeInner(DriverId const driv
buf->id = CanInterface::ODrive::ArbitrationId(driver, CANSimple::MSG_SET_INPUT_TORQUE);
buf->dataLength = 4;
buf->remote = false;
- memcpy(buf->msg.raw, &torque, sizeof(torque));
+ memcpy(buf->msg.raw, &torque_Nm, sizeof(torque_Nm));
CanInterface::SendPlainMessageNoFree(buf);
// Enable Torque Control Mode
@@ -730,7 +924,7 @@ GCodeResult HangprinterKinematics::SetODrive3PosMode(DriverId const driver, cons
if (estimate.valid)
{
float const desiredPos = estimate.value;
- CanMessageBuffer * buf = CanInterface::ODrive::PrepareSimpleMessage(driver, reply);
+ CanMessageBuffer* buf = CanInterface::ODrive::PrepareSimpleMessage(driver, reply);
if (buf == nullptr)
{
return GCodeResult::error;
@@ -758,11 +952,28 @@ GCodeResult HangprinterKinematics::SetODrive3PosMode(DriverId const driver, cons
#endif // DUAL_CAN
#if DUAL_CAN
-GCodeResult HangprinterKinematics::SetODrive3TorqueMode(DriverId const driver, float torque, const StringRef& reply) noexcept
+GCodeResult HangprinterKinematics::SetODrive3TorqueMode(DriverId const driver, float force_Newton, const StringRef& reply,
+ uint32_t setMechanicalAdvantage[], uint32_t setSpoolGearTeeth[],
+ uint32_t setMotorGearTeeth[], float setSpoolRadii[]) noexcept
{
+ static uint32_t mechanicalAdvantage_[HANGPRINTER_AXES] = { 0 };
+ static uint32_t spoolGearTeeth_[HANGPRINTER_AXES] = { 0 };
+ static uint32_t motorGearTeeth_[HANGPRINTER_AXES] = { 0 };
+ static float spoolRadii_[HANGPRINTER_AXES] = { 0.0 };
+ if (setMechanicalAdvantage != nullptr && setSpoolGearTeeth != nullptr &&
+ setMotorGearTeeth != nullptr && setSpoolRadii != nullptr) {
+ for(size_t i{0}; i < HANGPRINTER_AXES; ++i) {
+ mechanicalAdvantage_[i] = setMechanicalAdvantage[i];
+ spoolGearTeeth_[i] = setSpoolGearTeeth[i];
+ motorGearTeeth_[i] = setMotorGearTeeth[i];
+ spoolRadii_[i] = setSpoolRadii[i];
+ }
+ return GCodeResult::ok;
+ }
+
GCodeResult res = GCodeResult::ok;
- constexpr double MIN_TORQUE_NM = 0.0001;
- if (fabs(torque) < MIN_TORQUE_NM)
+ constexpr float MIN_TORQUE_N = 0.001;
+ if (fabsf(force_Newton) < MIN_TORQUE_N)
{
res = SetODrive3PosMode(driver, reply);
if (res == GCodeResult::ok)
@@ -772,22 +983,217 @@ GCodeResult HangprinterKinematics::SetODrive3TorqueMode(DriverId const driver, f
}
else
{
+ size_t const boardIndex = driver.boardAddress - 40;
+ if (boardIndex < 0 or boardIndex > 3) {
+ reply.catf("Board address not between 40 and 43: %d", driver.boardAddress);
+ return GCodeResult::error;
+ }
+
+ float const lineTension_N = force_Newton / mechanicalAdvantage_[boardIndex];
+ float const spoolTorque_Nm = lineTension_N * spoolRadii_[boardIndex] * 0.001;
+ float motorTorque_Nm = spoolTorque_Nm * motorGearTeeth_[boardIndex] / spoolGearTeeth_[boardIndex];
// Set the right sign
- torque = std::abs(torque);
+ motorTorque_Nm = std::abs(motorTorque_Nm);
if (driver.boardAddress == 40 || driver.boardAddress == 41) // Driver direction is not stored on main board!! (will be in the future)
{
- torque = -torque;
+ motorTorque_Nm = -motorTorque_Nm;
}
- res = SetODrive3TorqueModeInner(driver, torque, reply);
+ res = SetODrive3TorqueModeInner(driver, motorTorque_Nm, reply);
if (res == GCodeResult::ok)
{
- reply.catf("%.6f Nm, ", (double)torque);
+ reply.catf("%.6f Nm, ", (double)motorTorque_Nm);
}
}
return res;
}
#endif // DUAL_CAN
+
+float HangprinterKinematics::SpringK(float const springLength) const noexcept {
+ return springKPerUnitLength / springLength;
+}
+
+void HangprinterKinematics::StaticForces(float const machinePos[3], float F[4]) const noexcept {
+ if (moverWeight_kg > 0.0001) { // mover weight more than one gram
+ // Size of D-force in Newtons
+ float const mg = moverWeight_kg * 9.81;
+ // Unit vector directions toward each anchor from mover
+ float const normA = hyp3(anchors[A_AXIS], machinePos);
+ float const normB = hyp3(anchors[B_AXIS], machinePos);
+ float const normC = hyp3(anchors[C_AXIS], machinePos);
+ float const normD = hyp3(anchors[D_AXIS], machinePos);
+ float ax = (anchors[A_AXIS][0] - machinePos[0])/normA;
+ float ay = (anchors[A_AXIS][1] - machinePos[1])/normA;
+ float az = (anchors[A_AXIS][2] - machinePos[2])/normA;
+ float bx = (anchors[B_AXIS][0] - machinePos[0])/normB;
+ float by = (anchors[B_AXIS][1] - machinePos[1])/normB;
+ float bz = (anchors[B_AXIS][2] - machinePos[2])/normB;
+ float cx = (anchors[C_AXIS][0] - machinePos[0])/normC;
+ float cy = (anchors[C_AXIS][1] - machinePos[1])/normC;
+ float cz = (anchors[C_AXIS][2] - machinePos[2])/normC;
+ float dx = (anchors[D_AXIS][0] - machinePos[0])/normD;
+ float dy = (anchors[D_AXIS][1] - machinePos[1])/normD;
+ float dz = (anchors[D_AXIS][2] - machinePos[2])/normD;
+
+ float D_mg = 0.0F;
+ float D_pre = 0.0F;
+ if (dz > 0.0001) {
+ D_mg = mg / dz;
+ D_pre = targetForce_Newton;
+ }
+ // The D-forces' z-component is always equal to mg + targetForce_Newton.
+ // This means ABC-motors combined pull downwards targetForce_Newton N.
+ // I don't know if that's always solvable.
+ // Still, my tests show that we get very reasonable flex compensation...
+
+ // Right hand side of the equation
+ // A + B + C + D + (0,0,-mg)' = 0
+ // <=> A + B + C = -D + (0,0,mg)'
+ //
+ // Mx = y,
+ //
+ // Where M is the matrix
+ //
+ // ax bx cx
+ // M = ay by cy,
+ // az bz cz
+ //
+ // and x is the sizes of the forces:
+ //
+ // A
+ // x = B,
+ // C
+ //
+ // anc y is
+ //
+ // -D*dx
+ // y = -D*dy .
+ // -D*dz + mg
+ //
+ float const yx_mg = -D_mg*dx;
+ float const yy_mg = -D_mg*dy;
+ float const yz_mg = -D_mg*dz + mg;
+ float const yx_pre = -D_pre*dx;
+ float const yy_pre = -D_pre*dy;
+ float const yz_pre = -D_pre*dz;
+
+ // Start with saving us from dividing by zero during Gaussian substitution
+ float constexpr eps = 0.00001;
+ bool const divZero0 = std::abs(ax) < eps;
+ if (divZero0) {
+ float const tmpx = bx;
+ float const tmpy = by;
+ float const tmpz = bz;
+ bx = ax;
+ by = ay;
+ bz = az;
+ ax = tmpx;
+ ay = tmpy;
+ az = tmpz;
+ }
+ bool const divZero1 = (std::abs(by - (bx / ax) * ay) < eps);
+ if (divZero1) {
+ float const tmpx = cx;
+ float const tmpy = cy;
+ float const tmpz = cz;
+ cx = bx;
+ cy = by;
+ cz = bz;
+ bx = tmpx;
+ by = tmpy;
+ bz = tmpz;
+ }
+ bool const divZero2 = std::abs((cz - (cx / ax) * az) - ((cy - (cx / ax) * ay) / (by - (bx / ax) * ay)) * (bz - (bx / ax) * az)) < eps;
+ if (divZero2) {
+ float const tmpx = ax;
+ float const tmpy = ay;
+ float const tmpz = az;
+ ax = cx;
+ ay = cy;
+ az = cz;
+ cx = tmpx;
+ cy = tmpy;
+ cz = tmpz;
+ }
+
+ // Solving the two systems by Gaussian substitution
+ float const q0 = bx / ax;
+ float const q1 = cx / ax;
+ float const q2_mg = yx_mg / ax;
+ float const q2_pre = yx_pre / ax;
+ float const q3 = by - q0 * ay;
+ float const q4 = cy - q1 * ay;
+ float const q5_mg = yy_mg - q2_mg * ay;
+ float const q5_pre = yy_pre - q2_pre * ay;
+ float const q6 = bz - q0 * az;
+ float const q7 = cz - q1 * az;
+ float const q8_mg = yz_mg - q2_mg * az;
+ float const q8_pre = yz_pre - q2_pre * az;
+ float const q9 = q4 / q3;
+ float const q10_mg = q5_mg / q3;
+ float const q10_pre = q5_pre / q3;
+ float const q11 = q7 - q9 * q6;
+ float const q12_mg = q8_mg - q10_mg * q6;
+ float const q12_pre = q8_pre - q10_pre * q6;
+ float const q13_mg = q12_mg / q11;
+ float const q13_pre = q12_pre / q11;
+ float const q14_mg = q10_mg - q13_mg * q9;
+ float const q14_pre = q10_pre - q13_pre * q9;
+ float const q15_mg = q2_mg - q13_mg * q1;
+ float const q15_pre = q2_pre - q13_pre * q1;
+
+ // Size of the undetermined forces
+ float A_mg = q15_mg - q14_mg * q0;
+ float A_pre = q15_pre - q14_pre * q0;
+ float B_mg = q14_mg;
+ float B_pre = q14_pre;
+ float C_mg = q13_mg;
+ float C_pre = q13_pre;
+
+ if (divZero2) {
+ float const tmp_mg = A_mg;
+ A_mg = C_mg;
+ C_mg = tmp_mg;
+ float const tmp_pre = A_pre;
+ A_pre = C_pre;
+ C_pre = tmp_pre;
+ }
+ if (divZero1) {
+ float const tmp_mg = C_mg;
+ C_mg = B_mg;
+ B_mg = tmp_mg;
+ float const tmp_pre = C_pre;
+ C_pre = B_pre;
+ B_pre = tmp_pre;
+ }
+ if (divZero0) {
+ float const tmp_mg = B_mg;
+ B_mg = A_mg;
+ A_mg = tmp_mg;
+ float const tmp_pre = B_pre;
+ B_pre = A_pre;
+ A_pre = tmp_pre;
+ }
+
+ // Assure at least targetForce in the ABC lines (first argument to outer min()),
+ // and that no line get more than max planned force (second argument to outer min()).
+ float const preFac = min(max(std::abs((targetForce_Newton - C_mg) / C_pre),
+ max(std::abs((targetForce_Newton - B_mg) / B_pre), std::abs((targetForce_Newton - A_mg) / A_pre))),
+ min(min(std::abs((maxPlannedForce_Newton[A_AXIS] - A_mg) / A_pre), std::abs((maxPlannedForce_Newton[B_AXIS] - B_mg) / B_pre)),
+ min(std::abs((maxPlannedForce_Newton[C_AXIS] - C_mg) / C_pre), std::abs((maxPlannedForce_Newton[D_AXIS] - D_mg) / D_pre))));
+
+ float const A_tot = A_mg + preFac * A_pre;
+ float const B_tot = B_mg + preFac * B_pre;
+ float const C_tot = C_mg + preFac * C_pre;
+ float const D_tot = D_mg + preFac * D_pre;
+
+ F[0] = max(A_tot, minPlannedForce_Newton[A_AXIS]);
+ F[1] = max(B_tot, minPlannedForce_Newton[B_AXIS]);
+ F[2] = max(C_tot, minPlannedForce_Newton[C_AXIS]);
+ F[3] = max(D_tot, minPlannedForce_Newton[D_AXIS]);
+ }
+}
+
#endif // SUPPORT_HANGPRINTER
// End
diff --git a/src/Movement/Kinematics/HangprinterKinematics.h b/src/Movement/Kinematics/HangprinterKinematics.h
index 8584a389..8280e732 100644
--- a/src/Movement/Kinematics/HangprinterKinematics.h
+++ b/src/Movement/Kinematics/HangprinterKinematics.h
@@ -41,14 +41,19 @@ public:
bool WriteResumeSettings(FileStore *f) const noexcept override;
#endif
#if DUAL_CAN
+ static GCodeResult ReadODrive3AxisForce(DriverId driver, const StringRef& reply,
+ float setTorqueConstants[] = nullptr, uint32_t setMechanicalAdvantage[] = nullptr,
+ uint32_t setSpoolGearTeeth[] = nullptr, uint32_t setMotorGearTeeth[] = nullptr,
+ float setSpoolRadii[] = nullptr) THROWS(GCodeException);
static GCodeResult ReadODrive3Encoder(DriverId driver, GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException);
- static GCodeResult SetODrive3TorqueMode(DriverId driver, float torque, const StringRef& reply) noexcept;
+ static GCodeResult SetODrive3TorqueMode(DriverId driver, float force_Newton, const StringRef& reply,
+ uint32_t setMechanicalAdvantage[] = nullptr,
+ uint32_t setSpoolGearTeeth[] = nullptr, uint32_t setMotorGearTeeth[] = nullptr,
+ float setSpoolRadii[] = nullptr) noexcept;
#endif
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(anchors)
- OBJECT_MODEL_ARRAY(anchorCoordinates)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
// Basic facts about movement system
@@ -60,23 +65,54 @@ private:
void Init() noexcept;
void Recalc() noexcept;
- float LineLengthSquared(const float machinePos[3], const float anchor[3]) const noexcept; // Calculate the square of the line length from a spool from a Cartesian coordinate
void ForwardTransform(float a, float b, float c, float d, float machinePos[3]) const noexcept;
float MotorPosToLinePos(const int32_t motorPos, size_t axis) const noexcept;
- void PrintParameters(const StringRef& reply) const noexcept; // Print all the parameters for debugging
+ void PrintParameters(const StringRef& reply) const noexcept; // Print all the parameters for debugging
- float anchors[HANGPRINTER_AXES][3]; // XYZ coordinates of the anchors
- float printRadius;
- // Line buildup compensation
- float spoolBuildupFactor;
- float spoolRadii[HANGPRINTER_AXES];
- uint32_t mechanicalAdvantage[HANGPRINTER_AXES], linesPerSpool[HANGPRINTER_AXES];
- uint32_t motorGearTeeth[HANGPRINTER_AXES], spoolGearTeeth[HANGPRINTER_AXES], fullStepsPerMotorRev[HANGPRINTER_AXES];
+ // The real defaults are in the cpp file
+ float printRadius = 0.0F;
+ float anchors[HANGPRINTER_AXES][3] = {{ 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0},
+ { 0.0, 0.0, 0.0}};
+
+ // Line buildup compensation configurables
+
+ /* The real defaults are in the Init() function, since we sometimes need to reset
+ * defaults during runtime */
+ float spoolBuildupFactor = 0.0F;
+ float spoolRadii[HANGPRINTER_AXES] = { 0.0F };
+ uint32_t mechanicalAdvantage[HANGPRINTER_AXES] = { 0 };
+ uint32_t linesPerSpool[HANGPRINTER_AXES] = { 0 };
+ uint32_t motorGearTeeth[HANGPRINTER_AXES] = { 0 };
+ uint32_t spoolGearTeeth[HANGPRINTER_AXES] = { 0 };
+ uint32_t fullStepsPerMotorRev[HANGPRINTER_AXES] = { 0 };
+
+ // Flex compensation configurables
+ float moverWeight_kg = 0.0F;
+ float springKPerUnitLength = 0.0F;
+ float minPlannedForce_Newton[HANGPRINTER_AXES] = { 0.0F };
+ float maxPlannedForce_Newton[HANGPRINTER_AXES] = { 0.0F };
+ float guyWireLengths[HANGPRINTER_AXES] = { 0.0F };
+ float targetForce_Newton = 0.0F;
+ float torqueConstants[HANGPRINTER_AXES] = { 0.0F };
// Derived parameters
- float k0[HANGPRINTER_AXES], spoolRadiiSq[HANGPRINTER_AXES], k2[HANGPRINTER_AXES], lineLengthsOrigin[HANGPRINTER_AXES];
- float printRadiusSquared;
+ float k0[HANGPRINTER_AXES] = { 0.0F };
+ float spoolRadiiSq[HANGPRINTER_AXES] = { 0.0F };
+ float k2[HANGPRINTER_AXES] = { 0.0F };
+ float distancesOrigin[HANGPRINTER_AXES] = { 0.0F };
+ float springKsOrigin[HANGPRINTER_AXES] = { 0.0F };
+ float relaxedSpringLengthsOrigin[HANGPRINTER_AXES] = { 0.0F };
+ float fOrigin[HANGPRINTER_AXES] = { 0.0F };
+ float printRadiusSquared = 0.0F;
+
+ float SpringK(float const springLength) const noexcept;
+ void StaticForces(float const machinePos[3], float F[4]) const noexcept;
+ void flexDistances(float const machinePos[3], float const distanceA,
+ float const distanceB, float const distanceC,
+ float const distanceD, float flex[HANGPRINTER_AXES]) const noexcept;
#if DUAL_CAN
// Some CAN helpers
@@ -84,8 +120,9 @@ private:
bool valid = false;
float value = 0.0;
};
+ static ODriveAnswer GetODrive3MotorCurrent(DriverId driver, const StringRef& reply) THROWS(GCodeException);
static ODriveAnswer GetODrive3EncoderEstimate(DriverId driver, bool makeReference, const StringRef& reply, bool subtractReference) THROWS(GCodeException);
- static GCodeResult SetODrive3TorqueModeInner(DriverId driver, float torque, const StringRef& reply) noexcept;
+ static GCodeResult SetODrive3TorqueModeInner(DriverId driver, float torque_Nm, const StringRef& reply) noexcept;
static GCodeResult SetODrive3PosMode(DriverId driver, const StringRef& reply) noexcept;
#endif
};
diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.cpp b/src/Movement/Kinematics/LinearDeltaKinematics.cpp
index e0d2ff10..19b78324 100644
--- a/src/Movement/Kinematics/LinearDeltaKinematics.cpp
+++ b/src/Movement/Kinematics/LinearDeltaKinematics.cpp
@@ -24,13 +24,18 @@
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(LinearDeltaKinematics, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor LinearDeltaKinematics::towersArrayDescriptor =
+constexpr ObjectModelArrayTableEntry LinearDeltaKinematics::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const LinearDeltaKinematics*)self)->numTowers; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 1); }
+ // 10. Towers
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const LinearDeltaKinematics*)self)->numTowers; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 1); }
+ }
};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE_WITH_PARENT(LinearDeltaKinematics, RoundBedKinematics, 10)
+
constexpr ObjectModelTableEntry LinearDeltaKinematics::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
@@ -39,7 +44,7 @@ constexpr ObjectModelTableEntry LinearDeltaKinematics::objectModelTable[] =
{ "homedHeight", OBJECT_MODEL_FUNC(self->homedHeight, 3), ObjectModelEntryFlags::none },
{ "name", OBJECT_MODEL_FUNC(self->GetName(true)), ObjectModelEntryFlags::none },
{ "printRadius", OBJECT_MODEL_FUNC(self->printRadius, 1), ObjectModelEntryFlags::none },
- { "towers", OBJECT_MODEL_FUNC_NOSELF(&towersArrayDescriptor), ObjectModelEntryFlags::none },
+ { "towers", OBJECT_MODEL_FUNC_ARRAY(10), ObjectModelEntryFlags::none },
{ "xTilt", OBJECT_MODEL_FUNC(self->xTilt, 3), ObjectModelEntryFlags::none },
{ "yTilt", OBJECT_MODEL_FUNC(self->yTilt, 3), ObjectModelEntryFlags::none },
@@ -53,7 +58,7 @@ constexpr ObjectModelTableEntry LinearDeltaKinematics::objectModelTable[] =
constexpr uint8_t LinearDeltaKinematics::objectModelTableDescriptor[] = { 2, 7, 5 };
-DEFINE_GET_OBJECT_MODEL_TABLE(LinearDeltaKinematics)
+DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(LinearDeltaKinematics, RoundBedKinematics)
#endif
@@ -856,7 +861,7 @@ bool LinearDeltaKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
if (gb.Seen('B'))
{
- printRadius = gb.GetFValue();
+ printRadius = gb.GetPositiveFValue();
// Set the axis limits so that DWC reports them correctly (they are not otherwise used for deltas, except Z min)
Platform& p = reprap.GetPlatform();
p.SetAxisMinimum(X_AXIS, -printRadius, false);
diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.h b/src/Movement/Kinematics/LinearDeltaKinematics.h
index 8bd55d5d..5652ad7a 100644
--- a/src/Movement/Kinematics/LinearDeltaKinematics.h
+++ b/src/Movement/Kinematics/LinearDeltaKinematics.h
@@ -58,8 +58,7 @@ public:
float GetTowerY(size_t axis) const noexcept { return towerY[axis]; }
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(towers)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
void Init() noexcept;
diff --git a/src/Movement/Kinematics/PolarKinematics.cpp b/src/Movement/Kinematics/PolarKinematics.cpp
index 1e00c145..34f3481e 100644
--- a/src/Movement/Kinematics/PolarKinematics.cpp
+++ b/src/Movement/Kinematics/PolarKinematics.cpp
@@ -33,7 +33,7 @@ constexpr ObjectModelTableEntry PolarKinematics::objectModelTable[] =
constexpr uint8_t PolarKinematics::objectModelTableDescriptor[] = { 1, 1 };
-DEFINE_GET_OBJECT_MODEL_TABLE(PolarKinematics)
+DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(PolarKinematics, Kinematics)
#endif
diff --git a/src/Movement/Kinematics/RotaryDeltaKinematics.cpp b/src/Movement/Kinematics/RotaryDeltaKinematics.cpp
index eb2b8fc3..fcb0bb5d 100644
--- a/src/Movement/Kinematics/RotaryDeltaKinematics.cpp
+++ b/src/Movement/Kinematics/RotaryDeltaKinematics.cpp
@@ -35,7 +35,7 @@ constexpr ObjectModelTableEntry RotaryDeltaKinematics::objectModelTable[] =
constexpr uint8_t RotaryDeltaKinematics::objectModelTableDescriptor[] = { 1, 1 };
-DEFINE_GET_OBJECT_MODEL_TABLE(RotaryDeltaKinematics)
+DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(RotaryDeltaKinematics, RoundBedKinematics)
#endif
@@ -104,37 +104,20 @@ bool RotaryDeltaKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
{
bool seen = false;
size_t numValues = 3;
- if (gb.TryGetFloatArray('U', numValues, armLengths, reply, seen, true))
- {
- error = true;
- return true;
- }
-
+ gb.TryGetFloatArray('U', numValues, armLengths, seen, true);
numValues = 3;
- if (gb.TryGetFloatArray('L', numValues, rodLengths, reply, seen, true))
- {
- error = true;
- return true;
- }
+ gb.TryGetFloatArray('L', numValues, rodLengths, seen, true);
numValues = 3;
- if (gb.TryGetFloatArray('H', numValues, bearingHeights, reply, seen, true))
- {
- error = true;
- return true;
- }
+ gb.TryGetFloatArray('H', numValues, bearingHeights, seen, true);
numValues = 2;
- if (gb.TryGetFloatArray('A', numValues, minMaxArmAngles, reply, seen, false))
- {
- error = true;
- return true;
- }
+ gb.TryGetFloatArray('A', numValues, minMaxArmAngles, seen, false);
gb.TryGetFValue('R', radius, seen);
if (gb.Seen('B'))
{
- printRadius = gb.GetFValue();
+ printRadius = gb.GetPositiveFValue();
// Set the axis limits so that DWC reports them correctly (they are not otherwise used for deltas, except Z min)
Platform& p = reprap.GetPlatform();
p.SetAxisMinimum(X_AXIS, -printRadius, false);
diff --git a/src/Movement/Kinematics/ScaraKinematics.cpp b/src/Movement/Kinematics/ScaraKinematics.cpp
index 2ff4597e..e9485475 100644
--- a/src/Movement/Kinematics/ScaraKinematics.cpp
+++ b/src/Movement/Kinematics/ScaraKinematics.cpp
@@ -203,21 +203,9 @@ bool ScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const Strin
gb.TryGetFValue('D', distalArmLength, seen);
gb.TryGetFValue('X', xOffset, seen);
gb.TryGetFValue('Y', yOffset, seen);
- if (gb.TryGetFloatArray('A', 2, thetaLimits, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetFloatArray('B', 2, psiLimits, reply, seen))
- {
- error = true;
- return true;
- }
- if (gb.TryGetFloatArray('C', 3, crosstalk, reply, seen))
- {
- error = true;
- return true;
- }
+ gb.TryGetFloatArray('A', 2, thetaLimits, seen);
+ gb.TryGetFloatArray('B', 2, psiLimits, seen);
+ gb.TryGetFloatArray('C', 3, crosstalk, seen);
gb.TryGetFValue('R', requestedMinRadius, seen);
if (seen)
diff --git a/src/Movement/Kinematics/ZLeadscrewKinematics.cpp b/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
index 44ade884..3a0676dc 100644
--- a/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
+++ b/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
@@ -23,29 +23,32 @@ const float M3ScrewPitch = 0.5;
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(ZLeadscrewKinematics, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor ZLeadscrewKinematics::lastCorrectionsArrayDescriptor =
+constexpr ObjectModelArrayTableEntry ZLeadscrewKinematics::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const ZLeadscrewKinematics*)self)->numLeadscrews; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const ZLeadscrewKinematics*)self)->lastCorrections[context.GetLastIndex()], 3); }
-};
-
-constexpr ObjectModelArrayDescriptor ZLeadscrewKinematics::screwXArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const ZLeadscrewKinematics*)self)->numLeadscrews; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const ZLeadscrewKinematics*)self)->leadscrewX[context.GetLastIndex()], 1); }
+ // 10. Latest corrections
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const ZLeadscrewKinematics*)self)->numLeadscrews; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const ZLeadscrewKinematics*)self)->lastCorrections[context.GetLastIndex()], 3); }
+ },
+ // 11. Screw X coordinates
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const ZLeadscrewKinematics*)self)->numLeadscrews; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const ZLeadscrewKinematics*)self)->leadscrewX[context.GetLastIndex()], 1); }
+ },
+ // 12. Screw Y coordinates
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const ZLeadscrewKinematics*)self)->numLeadscrews; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const ZLeadscrewKinematics*)self)->leadscrewY[context.GetLastIndex()], 1); }
+ }
};
-constexpr ObjectModelArrayDescriptor ZLeadscrewKinematics::screwYArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const ZLeadscrewKinematics*)self)->numLeadscrews; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const ZLeadscrewKinematics*)self)->leadscrewY[context.GetLastIndex()], 1); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE_WITH_PARENT(ZLeadscrewKinematics, Kinematics, 10)
constexpr ObjectModelTableEntry ZLeadscrewKinematics::objectModelTable[] =
{
@@ -55,11 +58,11 @@ constexpr ObjectModelTableEntry ZLeadscrewKinematics::objectModelTable[] =
// 1. tiltCorrection members
{ "correctionFactor", OBJECT_MODEL_FUNC(self->correctionFactor, 1), ObjectModelEntryFlags::none },
- { "lastCorrections", OBJECT_MODEL_FUNC_NOSELF(&lastCorrectionsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "lastCorrections", OBJECT_MODEL_FUNC_ARRAY(10), ObjectModelEntryFlags::none },
{ "maxCorrection", OBJECT_MODEL_FUNC(self->maxCorrection, 1), ObjectModelEntryFlags::none },
{ "screwPitch", OBJECT_MODEL_FUNC(self->screwPitch, 2), ObjectModelEntryFlags::none },
- { "screwX", OBJECT_MODEL_FUNC_NOSELF(&screwXArrayDescriptor), ObjectModelEntryFlags::none },
- { "screwY", OBJECT_MODEL_FUNC_NOSELF(&screwYArrayDescriptor), ObjectModelEntryFlags::none },
+ { "screwX", OBJECT_MODEL_FUNC_ARRAY(11), ObjectModelEntryFlags::none },
+ { "screwY", OBJECT_MODEL_FUNC_ARRAY(12), ObjectModelEntryFlags::none },
};
constexpr uint8_t ZLeadscrewKinematics::objectModelTableDescriptor[] = { 2, 1, 6 };
diff --git a/src/Movement/Kinematics/ZLeadscrewKinematics.h b/src/Movement/Kinematics/ZLeadscrewKinematics.h
index 4864204b..1d5b3599 100644
--- a/src/Movement/Kinematics/ZLeadscrewKinematics.h
+++ b/src/Movement/Kinematics/ZLeadscrewKinematics.h
@@ -25,10 +25,7 @@ public:
#endif
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(screwX)
- OBJECT_MODEL_ARRAY(screwY)
- OBJECT_MODEL_ARRAY(lastCorrections)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
void AppendCorrections(const floatc_t corrections[], const StringRef& reply) const noexcept;
diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp
index 03d002d3..c8aed193 100644
--- a/src/Movement/Move.cpp
+++ b/src/Movement/Move.cpp
@@ -60,62 +60,65 @@ Task<Move::MoveTaskStackWords> Move::moveTask;
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Move, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(...) OBJECT_MODEL_FUNC_IF_BODY(Move, __VA_ARGS__)
-static constexpr ObjectModelArrayDescriptor axesArrayDescriptor =
+constexpr ObjectModelArrayTableEntry Move::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&reprap.GetPlatform(), 3); }
-};
-
-static constexpr ObjectModelArrayDescriptor extrudersArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetNumExtruders(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&reprap.GetPlatform(), 4); }
-};
-
-constexpr ObjectModelArrayDescriptor Move::queueArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ARRAY_SIZE(rings); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&((const Move*)self)->rings[context.GetLastIndex()]); }
-};
+ // 0. Axes
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&reprap.GetPlatform(), 3); }
+ },
+ // 1. Extruders
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetNumExtruders(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&reprap.GetPlatform(), 4); }
+ },
+ // 2. Motion system queues
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ARRAY_SIZE(rings); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&((const Move*)self)->rings[context.GetLastIndex()]); }
+ }
#if SUPPORT_COORDINATE_ROTATION
-
-constexpr ObjectModelArrayDescriptor Move::rotationCentreArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(reprap.GetGCodes().GetRotationCentre(context.GetLastIndex())); }
+ ,
+ // 3. Rotation centre coordinates
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(reprap.GetGCodes().GetRotationCentre(context.GetLastIndex())); }
+ }
};
#endif
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(Move)
+
constexpr ObjectModelTableEntry Move::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. Move members
- { "axes", OBJECT_MODEL_FUNC_NOSELF(&axesArrayDescriptor), ObjectModelEntryFlags::live },
+ { "axes", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::live },
{ "calibration", OBJECT_MODEL_FUNC(self, 3), ObjectModelEntryFlags::none },
{ "compensation", OBJECT_MODEL_FUNC(self, 6), ObjectModelEntryFlags::none },
{ "currentMove", OBJECT_MODEL_FUNC(self, 2), ObjectModelEntryFlags::live },
- { "extruders", OBJECT_MODEL_FUNC_NOSELF(&extrudersArrayDescriptor), ObjectModelEntryFlags::live },
+ { "extruders", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::live },
{ "idle", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none },
{ "kinematics", OBJECT_MODEL_FUNC(self->kinematics), ObjectModelEntryFlags::none },
{ "limitAxes", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().LimitAxes()), ObjectModelEntryFlags::none },
{ "noMovesBeforeHoming", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().NoMovesBeforeHoming()), ObjectModelEntryFlags::none },
- { "printingAcceleration", OBJECT_MODEL_FUNC(InverseConvertAcceleration(self->maxPrintingAcceleration), 1), ObjectModelEntryFlags::none },
- { "queue", OBJECT_MODEL_FUNC_NOSELF(&queueArrayDescriptor), ObjectModelEntryFlags::none },
+ { "printingAcceleration", OBJECT_MODEL_FUNC_NOSELF(InverseConvertAcceleration(reprap.GetGCodes().GetPrimaryMaxPrintingAcceleration()), 1), ObjectModelEntryFlags::none },
+ { "queue", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::none },
#if SUPPORT_COORDINATE_ROTATION
{ "rotation", OBJECT_MODEL_FUNC(self, 44), ObjectModelEntryFlags::none },
#endif
{ "shaping", OBJECT_MODEL_FUNC(&self->axisShaper, 0), ObjectModelEntryFlags::none },
- { "speedFactor", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetSpeedFactor(), 2), ObjectModelEntryFlags::none },
- { "travelAcceleration", OBJECT_MODEL_FUNC(InverseConvertAcceleration(self->maxTravelAcceleration), 1), ObjectModelEntryFlags::none },
- { "virtualEPos", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetVirtualExtruderPosition(), 5), ObjectModelEntryFlags::live },
- { "workplaceNumber", OBJECT_MODEL_FUNC_NOSELF((int32_t)reprap.GetGCodes().GetWorkplaceCoordinateSystemNumber() - 1), ObjectModelEntryFlags::none },
- { "workspaceNumber", OBJECT_MODEL_FUNC_NOSELF((int32_t)reprap.GetGCodes().GetWorkplaceCoordinateSystemNumber()), ObjectModelEntryFlags::obsolete },
+ { "speedFactor", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetPrimarySpeedFactor(), 2), ObjectModelEntryFlags::none },
+ { "travelAcceleration", OBJECT_MODEL_FUNC_NOSELF(InverseConvertAcceleration(reprap.GetGCodes().GetPrimaryMaxTravelAcceleration()), 1), ObjectModelEntryFlags::none },
+ { "virtualEPos", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetCurrentMovementState(context).latestVirtualExtruderPosition, 5), ObjectModelEntryFlags::live },
+ { "workplaceNumber", OBJECT_MODEL_FUNC_NOSELF((int32_t)reprap.GetGCodes().GetPrimaryWorkplaceCoordinateSystemNumber() - 1), ObjectModelEntryFlags::none },
+ { "workspaceNumber", OBJECT_MODEL_FUNC_NOSELF((int32_t)reprap.GetGCodes().GetPrimaryWorkplaceCoordinateSystemNumber()), ObjectModelEntryFlags::obsolete },
// 1. Move.Idle members
{ "factor", OBJECT_MODEL_FUNC_NOSELF(reprap.GetPlatform().GetIdleCurrentFactor(), 1), ObjectModelEntryFlags::none },
@@ -124,6 +127,7 @@ constexpr ObjectModelTableEntry Move::objectModelTable[] =
// 2. move.currentMove members
{ "acceleration", OBJECT_MODEL_FUNC(self->GetAccelerationMmPerSecSquared(), 1), ObjectModelEntryFlags::live },
{ "deceleration", OBJECT_MODEL_FUNC(self->GetDecelerationMmPerSecSquared(), 1), ObjectModelEntryFlags::live },
+ { "extrusionRate", OBJECT_MODEL_FUNC(self->GetTotalExtrusionRate(), 2), ObjectModelEntryFlags::live },
# if SUPPORT_LASER
{ "laserPwm", OBJECT_MODEL_FUNC_IF_NOSELF(reprap.GetGCodes().GetMachineType() == MachineType::laser,
reprap.GetPlatform().GetLaserPwm(), 2), ObjectModelEntryFlags::live },
@@ -168,7 +172,7 @@ constexpr ObjectModelTableEntry Move::objectModelTable[] =
#if SUPPORT_COORDINATE_ROTATION
// 8. move.rotation members
{ "angle", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetRotationAngle()), ObjectModelEntryFlags::none },
- { "centre", OBJECT_MODEL_FUNC_NOSELF(&rotationCentreArrayDescriptor), ObjectModelEntryFlags::none },
+ { "centre", OBJECT_MODEL_FUNC_ARRAY(3), ObjectModelEntryFlags::none },
#endif
};
@@ -177,7 +181,7 @@ constexpr uint8_t Move::objectModelTableDescriptor[] =
9 + SUPPORT_COORDINATE_ROTATION,
17 + SUPPORT_WORKPLACE_COORDINATES,
2,
- 4 + SUPPORT_LASER,
+ 5 + SUPPORT_LASER,
3,
2,
2,
@@ -202,25 +206,24 @@ Move::Move() noexcept
#if SUPPORT_ASYNC_MOVES
heightController(nullptr),
#endif
- maxPrintingAcceleration(ConvertAcceleration(DefaultPrintingAcceleration)), maxTravelAcceleration(ConvertAcceleration(DefaultTravelAcceleration)),
jerkPolicy(0),
numCalibratedFactors(0)
{
// Kinematics must be set up here because GCodes::Init asks the kinematics for the assumed initial position
kinematics = Kinematics::Create(KinematicsType::cartesian); // default to Cartesian
- mainDDARing.Init1(InitialDdaRingLength);
+ rings[0].Init1(InitialDdaRingLength);
#if SUPPORT_ASYNC_MOVES
- auxDDARing.Init1(AuxDdaRingLength);
+ rings[1].Init1(AuxDdaRingLength);
#endif
DriveMovement::InitialAllocate(InitialNumDms);
}
void Move::Init() noexcept
{
- mainDDARing.Init2();
+ rings[0].Init2();
#if SUPPORT_ASYNC_MOVES
- auxDDARing.Init2();
+ rings[1].Init2();
auxMoveAvailable = false;
auxMoveLocked = false;
#endif
@@ -247,9 +250,9 @@ void Move::Init() noexcept
void Move::Exit() noexcept
{
StepTimer::DisableTimerInterrupt();
- mainDDARing.Exit();
+ rings[0].Exit();
#if SUPPORT_ASYNC_MOVES
- auxDDARing.Exit();
+ rings[1].Exit();
#endif
#if SUPPORT_LASER || SUPPORT_IOBITS
delete laserTask;
@@ -263,15 +266,16 @@ void Move::Exit() noexcept
for (;;)
{
// Recycle the DDAs for completed moves, checking for DDA errors to print if Move debug is enabled
- mainDDARing.RecycleDDAs();
+ rings[0].RecycleDDAs();
#if SUPPORT_ASYNC_MOVES
- auxDDARing.RecycleDDAs();
+ rings[1].RecycleDDAs();
#endif
- // See if we can add another move to the ring
bool moveRead = false;
- const bool canAddMove = mainDDARing.CanAddMove();
- if (canAddMove)
+
+ // See if we can add another move to ring 0
+ const bool canAddRing0Move = rings[0].CanAddMove();
+ if (canAddRing0Move)
{
// OK to add another move. First check if a special move is available.
if (bedLevellingMoveAvailable)
@@ -279,7 +283,7 @@ void Move::Exit() noexcept
moveRead = true;
if (simulationMode < SimulationMode::partial)
{
- if (mainDDARing.AddSpecialMove(reprap.GetPlatform().MaxFeedrate(Z_AXIS), specialMoveCoords))
+ if (rings[0].AddSpecialMove(reprap.GetPlatform().MaxFeedrate(Z_AXIS), specialMoveCoords))
{
const uint32_t now = millis();
const uint32_t timeWaiting = now - whenLastMoveAdded;
@@ -297,17 +301,17 @@ void Move::Exit() noexcept
{
// If there's a G Code move available, add it to the DDA ring for processing.
RawMove nextMove;
- if (reprap.GetGCodes().ReadMove(nextMove)) // if we have a new move
+ if (reprap.GetGCodes().ReadMove(0, nextMove)) // if we have a new move
{
moveRead = true;
- if (simulationMode < SimulationMode::partial) // in simulation mode partial, we don't process incoming moves beyond this point
+ if (simulationMode < SimulationMode::partial) // in simulation mode partial, we don't process incoming moves beyond this point
{
if (nextMove.moveType == 0)
{
- AxisAndBedTransform(nextMove.coords, nextMove.tool, true);
+ AxisAndBedTransform(nextMove.coords, nextMove.movementTool, true);
}
- if (mainDDARing.AddStandardMove(nextMove, !IsRawMotorMove(nextMove.moveType)))
+ if (rings[0].AddStandardMove(nextMove, !IsRawMotorMove(nextMove.moveType)))
{
const uint32_t now = millis();
const uint32_t timeWaiting = now - whenLastMoveAdded;
@@ -323,39 +327,70 @@ void Move::Exit() noexcept
}
}
- // Let the DDA ring process moves. Better to have a few moves in the queue so that we can do lookahead, hence the test on idleCount and idleTime.
- uint32_t nextPrepareDelay = mainDDARing.Spin(simulationMode, !canAddMove, millis() - whenLastMoveAdded >= mainDDARing.GetGracePeriod());
+ // Let ring 0 process moves. Better to have a few moves in the queue so that we can do lookahead, hence the test on idleCount and idleTime.
+ uint32_t nextPrepareDelay = rings[0].Spin(simulationMode, !canAddRing0Move, millis() - whenLastMoveAdded >= rings[0].GetGracePeriod());
#if SUPPORT_ASYNC_MOVES
+ const bool canAddRing1Move = rings[1].CanAddMove();
+ if (canAddRing1Move)
{
- bool waitingForAuxSpace = false;
if (auxMoveAvailable)
{
- if (auxDDARing.CanAddMove())
+ moveRead = true;
+ if (rings[1].AddAsyncMove(auxMove))
{
- if (auxDDARing.AddAsyncMove(auxMove))
+ const uint32_t now = millis();
+ const uint32_t timeWaiting = now - whenLastMoveAdded;
+ if (timeWaiting > longestGcodeWaitInterval)
{
- moveState = MoveState::collecting;
+ longestGcodeWaitInterval = timeWaiting;
}
- auxMoveAvailable = false;
- }
- else
- {
- waitingForAuxSpace = true;
+ whenLastMoveAdded = now;
+ moveState = MoveState::collecting;
}
+ auxMoveAvailable = false;
}
- const uint32_t auxPrepareDelay = auxDDARing.Spin(simulationMode, waitingForAuxSpace, true); // let the DDA ring process moves
- if (auxPrepareDelay < nextPrepareDelay)
+ else
{
- nextPrepareDelay = auxPrepareDelay;
+ // If there's a G Code move available, add it to the DDA ring for processing.
+ RawMove nextMove;
+ if (reprap.GetGCodes().ReadMove(1, nextMove)) // if we have a new move
+ {
+ moveRead = true;
+ if (simulationMode < SimulationMode::partial) // in simulation mode partial, we don't process incoming moves beyond this point
+ {
+ if (nextMove.moveType == 0)
+ {
+ AxisAndBedTransform(nextMove.coords, nextMove.movementTool, true);
+ }
+
+ if (rings[1].AddStandardMove(nextMove, !IsRawMotorMove(nextMove.moveType)))
+ {
+ const uint32_t now = millis();
+ const uint32_t timeWaiting = now - whenLastMoveAdded;
+ if (timeWaiting > longestGcodeWaitInterval)
+ {
+ longestGcodeWaitInterval = timeWaiting;
+ }
+ whenLastMoveAdded = now;
+ moveState = MoveState::collecting;
+ }
+ }
+ }
}
}
+
+ const uint32_t auxPrepareDelay = rings[1].Spin(simulationMode, !canAddRing1Move, millis() - whenLastMoveAdded >= rings[1].GetGracePeriod());
+ if (auxPrepareDelay < nextPrepareDelay)
+ {
+ nextPrepareDelay = auxPrepareDelay;
+ }
#endif
// Reduce motor current to standby if the rings have been idle for long enough
- if ( mainDDARing.IsIdle()
+ if ( rings[0].IsIdle()
#if SUPPORT_ASYNC_MOVES
- && auxDDARing.IsIdle()
+ && rings[1].IsIdle()
#endif
)
{
@@ -398,9 +433,9 @@ void Move::MoveAvailable() noexcept
}
// Tell the lookahead ring we are waiting for it to empty and return true if it is
-bool Move::WaitingForAllMovesFinished() noexcept
+bool Move::WaitingForAllMovesFinished(size_t queueNumber) noexcept
{
- return mainDDARing.SetWaitingToEmpty();
+ return rings[queueNumber].SetWaitingToEmpty();
}
// Return the number of actually probed probe points
@@ -415,7 +450,7 @@ float Move::PushBabyStepping(size_t axis, float amount) noexcept
{
TaskCriticalSectionLocker lock; // lock out the Move task
- return mainDDARing.PushBabyStepping(axis, amount);
+ return rings[0].PushBabyStepping(axis, amount);
}
// Change the kinematics to the specified type if it isn't already
@@ -451,17 +486,17 @@ bool Move::IsAccessibleProbePoint(float axesCoords[MaxAxes], AxesBitmap axes) co
}
// Pause the print as soon as we can, returning true if we are able to skip any moves and updating 'rp' to the first move we skipped.
-bool Move::PausePrint(RestorePoint& rp) noexcept
+bool Move::PausePrint(unsigned int queueNumber, RestorePoint& rp) noexcept
{
- return mainDDARing.PauseMoves(rp);
+ return rings[queueNumber].PauseMoves(rp);
}
#if HAS_VOLTAGE_MONITOR || HAS_STALL_DETECT
// Pause the print immediately, returning true if we were able to skip or abort any moves and setting up to the move we aborted
-bool Move::LowPowerOrStallPause(RestorePoint& rp) noexcept
+bool Move::LowPowerOrStallPause(unsigned int queueNumber, RestorePoint& rp) noexcept
{
- return mainDDARing.LowPowerOrStallPause(rp);
+ return rings[queueNumber].LowPowerOrStallPause(rp);
}
#endif
@@ -514,23 +549,21 @@ void Move::Diagnostics(MessageType mtype) noexcept
maxDelay = maxDelayIncrease = 0;
#endif
-#if SUPPORT_ASYNC_MOVES
- mainDDARing.Diagnostics(mtype, "Main");
- auxDDARing.Diagnostics(mtype, "Aux");
-#else
- mainDDARing.Diagnostics(mtype, "");
-#endif
+ for (size_t i = 0; i < ARRAY_SIZE(rings); ++i)
+ {
+ rings[i].Diagnostics(mtype, i);
+ }
}
// Set the current position to be this
-void Move::SetNewPosition(const float positionNow[MaxAxesPlusExtruders], bool doBedCompensation) noexcept
+void Move::SetNewPosition(const float positionNow[MaxAxesPlusExtruders], bool doBedCompensation, unsigned int queueNumber) noexcept
{
float newPos[MaxAxesPlusExtruders];
memcpyf(newPos, positionNow, ARRAY_SIZE(newPos)); // copy to local storage because Transform modifies it
- AxisAndBedTransform(newPos, reprap.GetCurrentTool(), doBedCompensation);
+ AxisAndBedTransform(newPos, reprap.GetGCodes().GetMovementState(queueNumber).currentTool, doBedCompensation);
- mainDDARing.SetLiveCoordinates(newPos);
- mainDDARing.SetPositions(newPos);
+ rings[queueNumber].SetLiveCoordinates(newPos);
+ rings[queueNumber].SetPositions(newPos);
}
// Convert distance to steps for a particular drive
@@ -753,7 +786,11 @@ void Move::SetIdentityTransform() noexcept
// Load the height map from file, returning true if an error occurred with the error reason appended to the buffer
bool Move::LoadHeightMapFromFile(FileStore *f, const char *fname, const StringRef& r) noexcept
{
- const bool err = heightMap.LoadFromFile(f, fname, r);
+ const bool err = heightMap.LoadFromFile(f, fname, r
+#if SUPPORT_PROBE_POINTS_FILE
+ , false // loading the height map, not the probe points file
+#endif
+ );
if (err)
{
heightMap.ClearGridHeights(); // make sure we don't end up with a partial height map
@@ -774,7 +811,22 @@ bool Move::SaveHeightMapToFile(FileStore *f, const char *fname) noexcept
return heightMap.SaveToFile(f, fname, zShift);
}
-#endif
+# if SUPPORT_PROBE_POINTS_FILE
+
+// Load the probe points map from a file returning true if an error occurred
+bool Move::LoadProbePointsFromFile(FileStore *f, const char *fname, const StringRef& r) noexcept
+{
+ return heightMap.LoadFromFile(f, fname, r, true);
+}
+
+void Move::ClearProbePointsInvalid() noexcept
+{
+ heightMap.ClearProbePointsInvalid();
+}
+
+# endif
+
+#endif // HAS_MASS_STORAGE || HAS_SBC_INTERFACE
void Move::SetTaperHeight(float h) noexcept
{
@@ -936,7 +988,7 @@ void Move::Simulate(SimulationMode simMode) noexcept
simulationMode = simMode;
if (simMode != SimulationMode::off)
{
- mainDDARing.ResetSimulationTime();
+ rings[0].ResetSimulationTime();
}
}
@@ -978,38 +1030,6 @@ bool Move::WriteResumeSettings(FileStore *f) const noexcept
#endif
-// Process M204
-GCodeResult Move::ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException)
-{
- bool seen = false;
- if (gb.Seen('S'))
- {
- // For backwards compatibility with old versions of Marlin (e.g. for Cura and the Prusa fork of slic3r), set both accelerations
- seen = true;
- maxTravelAcceleration = maxPrintingAcceleration = gb.GetAcceleration();
- }
- if (gb.Seen('P'))
- {
- seen = true;
- maxPrintingAcceleration = gb.GetAcceleration();
- }
- if (gb.Seen('T'))
- {
- seen = true;
- maxTravelAcceleration = gb.GetAcceleration();
- }
- if (seen)
- {
- reprap.MoveUpdated();
- }
- else
- {
- reply.printf("Maximum printing acceleration %.1f, maximum travel acceleration %.1f mm/sec^2",
- (double)InverseConvertAcceleration(maxPrintingAcceleration), (double)InverseConvertAcceleration(maxTravelAcceleration));
- }
- return GCodeResult::ok;
-}
-
// Process M595
GCodeResult Move::ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
@@ -1022,7 +1042,7 @@ GCodeResult Move::ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& rep
{
if (gb.Seen('S'))
{
- const float advance = gb.GetFValue();
+ const float advance = gb.GetNonNegativeFValue();
if (!reprap.GetGCodes().LockMovementAndWaitForStandstill(gb))
{
return GCodeResult::notFinished;
@@ -1062,7 +1082,7 @@ GCodeResult Move::ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& rep
}
else
{
- const Tool * const ct = reprap.GetCurrentTool();
+ const Tool * const ct = reprap.GetGCodes().GetConstMovementState(gb).currentTool;
if (ct == nullptr)
{
reply.copy("No tool selected");
@@ -1184,9 +1204,9 @@ void Move::RevertPosition(const CanMessageRevertPosition& msg) noexcept
// Interrupts are assumed enabled on entry
float Move::LiveCoordinate(unsigned int axisOrExtruder, const Tool *tool) noexcept
{
- if (mainDDARing.HaveLiveCoordinatesChanged())
+ if (rings[0].HaveLiveCoordinatesChanged())
{
- mainDDARing.LiveCoordinates(latestLiveCoordinates);
+ rings[0].LiveCoordinates(latestLiveCoordinates);
InverseAxisAndBedTransform(latestLiveCoordinates, tool);
}
return latestLiveCoordinates[axisOrExtruder];
@@ -1276,7 +1296,7 @@ void Move::LaserTaskRun() noexcept
# if SUPPORT_LASER
// Manage the laser power
uint32_t ticks;
- while ((ticks = mainDDARing.ManageLaserPower()) != 0)
+ while ((ticks = rings[0].ManageLaserPower()) != 0)
{
(void)TaskBase::Take(ticks);
}
diff --git a/src/Movement/Move.h b/src/Movement/Move.h
index a1414ada..27726ea5 100644
--- a/src/Movement/Move.h
+++ b/src/Movement/Move.h
@@ -58,14 +58,19 @@ public:
[[noreturn]] void MoveLoop() noexcept; // Main loop called by the Move task
void GetCurrentMachinePosition(float m[MaxAxes], bool disableMotorMapping) const noexcept; // Get the current position in untransformed coords
+#if SUPPORT_ASYNC_MOVES
+ void GetPartialMachinePosition(float m[MaxAxes], AxesBitmap whichAxes, unsigned int queueNumber) const noexcept; // Get the current position of some axes from one of the rings
+#endif
void GetCurrentUserPosition(float m[MaxAxes], uint8_t moveType, const Tool *tool) const noexcept;
// Return the position (after all queued moves have been executed) in transformed coords
int32_t GetEndPoint(size_t drive) const noexcept; // Get the current position of a motor
float LiveCoordinate(unsigned int axisOrExtruder, const Tool *tool) noexcept; // Gives the last point at the end of the last complete DDA
void MoveAvailable() noexcept; // Called from GCodes to tell the Move task that a move is available
- bool WaitingForAllMovesFinished() noexcept; // Tell the lookahead ring we are waiting for it to empty and return true if it is
- void DoLookAhead() noexcept SPEED_CRITICAL; // Run the look-ahead procedure
- void SetNewPosition(const float positionNow[MaxAxesPlusExtruders], bool doBedCompensation) noexcept; // Set the current position to be this
+ bool WaitingForAllMovesFinished(size_t queueNumber) noexcept
+ pre(queueNumber < rings.upb); // Tell the lookahead ring we are waiting for it to empty and return true if it is
+ void DoLookAhead() noexcept SPEED_CRITICAL; // Run the look-ahead procedure
+ void SetNewPosition(const float positionNow[MaxAxesPlusExtruders], bool doBedCompensation, unsigned int queueNumber) noexcept
+ pre(queueNumber < NumMovementSystems); // Set the current position to be this
void ResetExtruderPositions() noexcept; // Resets the extrusion amounts of the live coordinates
void SetXYBedProbePoint(size_t index, float x, float y) noexcept; // Record the X and Y coordinates of a probe point
void SetZBedProbePoint(size_t index, float z, bool wasXyCorrected, bool wasError) noexcept; // Record the Z coordinate of a probe point
@@ -92,7 +97,6 @@ public:
float PushBabyStepping(size_t axis, float amount) noexcept; // Try to push some babystepping through the lookahead queue
- GCodeResult ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException); // process M204
GCodeResult ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // process M595
GCodeResult ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // process M572
@@ -102,8 +106,6 @@ public:
GCodeResult EutSetRemotePressureAdvance(const CanMessageMultipleDrivesRequest<float>& msg, size_t dataLength, const StringRef& reply) noexcept;
#endif
- float GetMaxPrintingAcceleration() const noexcept { return maxPrintingAcceleration; }
- float GetMaxTravelAcceleration() const noexcept { return maxTravelAcceleration; }
AxisShaper& GetAxisShaper() noexcept { return axisShaper; }
ExtruderShaper& GetExtruderShaper(size_t extruder) noexcept { return extruderShapers[extruder]; }
@@ -132,18 +134,18 @@ public:
void SetIdleTimeout(float timeout) noexcept; // Set the idle timeout in seconds
void Simulate(SimulationMode simMode) noexcept; // Enter or leave simulation mode
- float GetSimulationTime() const noexcept { return mainDDARing.GetSimulationTime(); } // Get the accumulated simulation time
+ float GetSimulationTime() const noexcept { return rings[0].GetSimulationTime(); } // Get the accumulated simulation time
- bool PausePrint(RestorePoint& rp) noexcept; // Pause the print as soon as we can, returning true if we were able to
+ bool PausePrint(unsigned int queueNumber, RestorePoint& rp) noexcept; // Pause the print as soon as we can, returning true if we were able to
#if HAS_VOLTAGE_MONITOR || HAS_STALL_DETECT
- bool LowPowerOrStallPause(RestorePoint& rp) noexcept; // Pause the print immediately, returning true if we were able to
+ bool LowPowerOrStallPause(unsigned int queueNumber, RestorePoint& rp) noexcept; // Pause the print immediately, returning true if we were able to
#endif
- bool NoLiveMovement() const noexcept { return mainDDARing.IsIdle(); } // Is a move running, or are there any queued?
+ bool NoLiveMovement() const noexcept { return rings[0].IsIdle(); } // Is a move running, or are there any queued?
- uint32_t GetScheduledMoves() const noexcept { return mainDDARing.GetScheduledMoves(); } // How many moves have been scheduled?
- uint32_t GetCompletedMoves() const noexcept { return mainDDARing.GetCompletedMoves(); } // How many moves have been completed?
- void ResetMoveCounters() noexcept { mainDDARing.ResetMoveCounters(); }
+ uint32_t GetScheduledMoves() const noexcept { return rings[0].GetScheduledMoves(); } // How many moves have been scheduled?
+ uint32_t GetCompletedMoves() const noexcept { return rings[0].GetCompletedMoves(); } // How many moves have been completed?
+ void ResetMoveCounters() noexcept { rings[0].ResetMoveCounters(); }
HeightMap& AccessHeightMap() noexcept { return heightMap; } // Access the bed probing grid
const GridDefinition& GetGrid() const noexcept { return heightMap.GetGrid(); } // Get the grid definition
@@ -151,15 +153,20 @@ public:
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
bool LoadHeightMapFromFile(FileStore *f, const char *fname, const StringRef& r) noexcept; // Load the height map from a file returning true if an error occurred
bool SaveHeightMapToFile(FileStore *f, const char *fname) noexcept; // Save the height map to a file returning true if an error occurred
+# if SUPPORT_PROBE_POINTS_FILE
+ bool LoadProbePointsFromFile(FileStore *f, const char *fname, const StringRef& r) noexcept; // Load the probe points map from a file returning true if an error occurred
+ void ClearProbePointsInvalid() noexcept;
+# endif
#endif
const RandomProbePointSet& GetProbePoints() const noexcept { return probePoints; } // Return the probe point set constructed from G30 commands
- DDARing& GetMainDDARing() noexcept { return mainDDARing; }
- float GetTopSpeedMmPerSec() const noexcept { return mainDDARing.GetTopSpeedMmPerSec(); }
- float GetRequestedSpeedMmPerSec() const noexcept { return mainDDARing.GetRequestedSpeedMmPerSec(); }
- float GetAccelerationMmPerSecSquared() const noexcept { return mainDDARing.GetAccelerationMmPerSecSquared(); }
- float GetDecelerationMmPerSecSquared() const noexcept { return mainDDARing.GetDecelerationMmPerSecSquared(); }
+ DDARing& GetMainDDARing() noexcept { return rings[0]; }
+ float GetTopSpeedMmPerSec() const noexcept { return rings[0].GetTopSpeedMmPerSec(); }
+ float GetRequestedSpeedMmPerSec() const noexcept { return rings[0].GetRequestedSpeedMmPerSec(); }
+ float GetAccelerationMmPerSecSquared() const noexcept { return rings[0].GetAccelerationMmPerSecSquared(); }
+ float GetDecelerationMmPerSecSquared() const noexcept { return rings[0].GetDecelerationMmPerSecSquared(); }
+ float GetTotalExtrusionRate() const noexcept { return rings[0].GetTotalExtrusionRate(); }
void AdjustLeadscrews(const floatc_t corrections[]) noexcept; // Called by some Kinematics classes to adjust the leadscrews
@@ -169,7 +176,7 @@ public:
bool WriteResumeSettings(FileStore *f) const noexcept; // Write settings for resuming the print
#endif
- uint32_t ExtruderPrintingSince() const noexcept { return mainDDARing.ExtruderPrintingSince(); } // When we started doing normal moves after the most recent extruder-only move
+ uint32_t ExtruderPrintingSince() const noexcept { return rings[0].ExtruderPrintingSince(); } // When we started doing normal moves after the most recent extruder-only move
unsigned int GetJerkPolicy() const noexcept { return jerkPolicy; }
void SetJerkPolicy(unsigned int jp) noexcept { jerkPolicy = jp; }
@@ -204,13 +211,13 @@ public:
# if USE_REMOTE_INPUT_SHAPING
void AddShapeddMoveFromRemote(const CanMessageMovementLinearShaped& msg) noexcept // add a move from the ATE to the movement queue
{
- mainDDARing.AddMoveFromRemote(msg);
+ rings[0].AddMoveFromRemote(msg);
MoveAvailable();
}
# else
void AddMoveFromRemote(const CanMessageMovementLinear& msg) noexcept // add a move from the ATE to the movement queue
{
- mainDDARing.AddMoveFromRemote(msg);
+ rings[0].AddMoveFromRemote(msg);
MoveAvailable();
}
# endif
@@ -224,12 +231,7 @@ public:
#endif
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(queue)
-
-#if SUPPORT_COORDINATE_ROTATION
- OBJECT_MODEL_ARRAY(rotationCentre)
-#endif
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
enum class MoveState : uint8_t
@@ -254,25 +256,18 @@ private:
static constexpr unsigned int MoveTaskStackWords = 450;
static Task<MoveTaskStackWords> moveTask;
+ DDARing rings[NumMovementSystems];
+
#if SUPPORT_ASYNC_MOVES
- DDARing rings[2];
- DDARing& auxDDARing = rings[1]; // the DDA ring used for live babystepping, height following and other asynchronous moves
AsyncMove auxMove;
volatile bool auxMoveLocked;
volatile bool auxMoveAvailable;
HeightController *heightController;
-#else
- DDARing rings[1];
#endif
- DDARing& mainDDARing = rings[0]; // The DDA ring used for regular moves
-
SimulationMode simulationMode; // Are we simulating, or really printing?
MoveState moveState; // whether the idle timer is active
- float maxPrintingAcceleration;
- float maxTravelAcceleration;
-
unsigned int jerkPolicy; // When we allow jerk
unsigned int idleCount; // The number of times Spin was called and had no new moves to process
@@ -325,24 +320,34 @@ private:
// Get the current position in untransformed coords
inline void Move::GetCurrentMachinePosition(float m[MaxAxes], bool disableMotorMapping) const noexcept
{
- return mainDDARing.GetCurrentMachinePosition(m, disableMotorMapping);
+ return rings[0].GetCurrentMachinePosition(m, disableMotorMapping);
+}
+
+#if SUPPORT_ASYNC_MOVES
+
+// Get the current position of some axes from one of the rings
+inline void Move::GetPartialMachinePosition(float m[MaxAxes], AxesBitmap whichAxes, unsigned int queueNumber) const noexcept
+{
+ rings[queueNumber].GetPartialMachinePosition(m, whichAxes);
}
+#endif
+
// Get the current position of a motor
inline int32_t Move::GetEndPoint(size_t drive) const noexcept
{
- return mainDDARing.GetEndPoint(drive);
+ return rings[0].GetEndPoint(drive);
}
// Perform motor endpoint adjustment
inline void Move::AdjustMotorPositions(const float adjustment[], size_t numMotors) noexcept
{
- mainDDARing.AdjustMotorPositions(adjustment, numMotors);
+ rings[0].AdjustMotorPositions(adjustment, numMotors);
}
inline void Move::ResetExtruderPositions() noexcept
{
- mainDDARing.ResetExtruderPositions();
+ rings[0].ResetExtruderPositions();
}
inline float Move::GetPressureAdvanceClocks(size_t extruder) const noexcept
@@ -354,7 +359,7 @@ inline float Move::GetPressureAdvanceClocks(size_t extruder) const noexcept
// Returns the number of motor steps moves since the last call, and sets isPrinting true unless we are currently executing an extruding but non-printing move
inline int32_t Move::GetAccumulatedExtrusion(size_t drive, bool& isPrinting) noexcept
{
- return mainDDARing.GetAccumulatedMovement(drive, isPrinting);
+ return rings[0].GetAccumulatedMovement(drive, isPrinting);
}
#if HAS_SMART_DRIVERS
@@ -363,7 +368,7 @@ inline int32_t Move::GetAccumulatedExtrusion(size_t drive, bool& isPrinting) noe
// This is called from the stepper drivers SPI interface ISR
inline __attribute__((always_inline)) uint32_t Move::GetStepInterval(size_t axis, uint32_t microstepShift) const noexcept
{
- return (simulationMode == SimulationMode::off) ? mainDDARing.GetStepInterval(axis, microstepShift) : 0;
+ return (simulationMode == SimulationMode::off) ? rings[0].GetStepInterval(axis, microstepShift) : 0;
}
#endif
diff --git a/src/Movement/RawMove.cpp b/src/Movement/RawMove.cpp
index d1c1e65e..9d78fa10 100644
--- a/src/Movement/RawMove.cpp
+++ b/src/Movement/RawMove.cpp
@@ -6,6 +6,11 @@
*/
#include "RawMove.h"
+#include <GCodes/GCodes.h>
+#include <GCodes/GCodeQueue.h>
+#include <Platform/RepRap.h>
+#include <Platform/Platform.h>
+#include <Tools/Tool.h>
// Set up some default values in the move buffer for special moves, e.g. for Z probing and firmware retraction
void RawMove::SetDefaults(size_t firstDriveToZero) noexcept
@@ -18,10 +23,11 @@ void RawMove::SetDefaults(size_t firstDriveToZero) noexcept
checkEndstops = false;
reduceAcceleration = false;
hasPositiveExtrusion = false;
+ inverseTimeMode = false;
linearAxesMentioned = false;
rotationalAxesMentioned = false;
filePos = noFilePosition;
- tool = nullptr;
+ movementTool = nullptr;
cosXyAngle = 1.0;
for (size_t drive = firstDriveToZero; drive < MaxAxesPlusExtruders; ++drive)
{
@@ -34,6 +40,192 @@ float MovementState::GetProportionDone() const noexcept
return (float)(totalSegments - segmentsLeft)/(float)totalSegments;
}
+void MovementState::Reset() noexcept
+{
+ ClearMove();
+ filePos = noFilePosition;
+ codeQueue->Clear();
+ currentCoordinateSystem = 0;
+ pausedInMacro = false;
+
+ for (float& f : coords)
+ {
+ f = 0.0; // clear out all axis and extruder coordinates
+ }
+
+ maxPrintingAcceleration = ConvertAcceleration(DefaultPrintingAcceleration);
+ maxTravelAcceleration = ConvertAcceleration(DefaultTravelAcceleration);
+
+ currentZHop = 0.0; // clear this before calling ToolOffsetInverseTransform
+ movementTool = currentTool = nullptr;
+ latestVirtualExtruderPosition = moveStartVirtualExtruderPosition = 0.0;
+ virtualFanSpeed = 0.0;
+ speedFactor = 1.0;
+ newToolNumber = -1;
+ previousToolNumber = -1;
+
+#if SUPPORT_LASER || SUPPORT_IOBITS
+ laserPwmOrIoBits.Clear();
+#endif
+ ClearMove();
+ updateUserPositionGb = nullptr;
+ restartMoveFractionDone = 0.0;
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
+ fileOffsetToPrint = 0;
+#endif
+ for (RestorePoint& rp : restorePoints)
+ {
+ rp.Init();
+ }
+ InitObjectCancellation();
+}
+
+void MovementState::ChangeExtrusionFactor(unsigned int extruder, float multiplier) noexcept
+{
+ if (segmentsLeft != 0 && applyM220M221)
+ {
+ coords[ExtruderToLogicalDrive(extruder)] *= multiplier; // last move not gone, so update it
+ }
+}
+
+void MovementState::ClearMove() noexcept
+{
+ TaskCriticalSectionLocker lock; // make sure that other tasks sees a consistent memory state
+
+ segmentsLeft = 0;
+ segMoveState = SegmentedMoveState::inactive;
+ doingArcMove = false;
+ checkEndstops = false;
+ reduceAcceleration = false;
+ moveType = 0;
+ applyM220M221 = false;
+ moveFractionToSkip = 0.0;
+}
+
+void MovementState::Diagnostics(MessageType mtype, unsigned int moveSystemNumber) noexcept
+{
+ reprap.GetPlatform().MessageF(mtype, "Q%u segments left %u"
+#if SUPPORT_ASYNC_MOVES
+ ", axes/extruders owned %03x"
+#endif
+ "\n",
+ moveSystemNumber,
+ segmentsLeft
+#if SUPPORT_ASYNC_MOVES
+ , (unsigned int)axesAndExtrudersOwned.GetRaw()
+#endif
+ );
+ codeQueue->Diagnostics(mtype, moveSystemNumber);
+}
+
+void MovementState::SavePosition(unsigned int restorePointNumber, size_t numAxes, float p_feedRate, FilePosition p_filePos) noexcept
+{
+ RestorePoint& rp = restorePoints[restorePointNumber];
+ for (size_t axis = 0; axis < numAxes; ++axis)
+ {
+ rp.moveCoords[axis] = currentUserPosition[axis];
+ }
+
+ rp.feedRate = p_feedRate;
+ rp.virtualExtruderPosition = latestVirtualExtruderPosition;
+ rp.filePos = p_filePos;
+ rp.toolNumber = GetCurrentToolNumber();
+ rp.fanSpeed = virtualFanSpeed;
+
+#if SUPPORT_LASER || SUPPORT_IOBITS
+ rp.laserPwmOrIoBits = laserPwmOrIoBits;
+#endif
+}
+
+// Select the specified tool, putting the existing current tool into standby
+void MovementState::SelectTool(int toolNumber, bool simulating) noexcept
+{
+ ReadLockedPointer<Tool> const newTool = Tool::GetLockedTool(toolNumber);
+ if (!simulating && currentTool != nullptr && currentTool != newTool.Ptr())
+ {
+ currentTool->Standby();
+ }
+ currentTool = newTool.Ptr(); // must do this first so that Activate() will always work
+ if (!simulating && newTool.IsNotNull())
+ {
+ newTool->Activate();
+ }
+}
+
+// Get a locked pointer to the current tool, or null if there is no current tool
+ReadLockedPointer<Tool> MovementState::GetLockedCurrentTool() const noexcept
+{
+ return ReadLockedPointer<Tool>(Tool::toolListLock, currentTool);
+}
+
+// Get the current tool, or failing that the default tool. May return nullptr if there are no tools.
+// Called when a M104 or M109 command doesn't specify a tool number.
+ReadLockedPointer<Tool> MovementState::GetLockedCurrentOrDefaultTool() const noexcept
+{
+ // If a tool is already selected, use that one, else use the lowest-numbered tool which is the one at the start of the tool list
+ return ReadLockedPointer<Tool>(Tool::toolListLock, (currentTool != nullptr) ? currentTool : Tool::GetToolList());
+}
+
+// Return the current tool number, or -1 if no tool selected
+int MovementState::GetCurrentToolNumber() const noexcept
+{
+ return (currentTool == nullptr) ? -1 : currentTool->Number();
+}
+
+// Set the previous tool number. Inline because it is only called from one place.
+void MovementState::SetPreviousToolNumber() noexcept
+{
+ previousToolNumber = (currentTool != nullptr) ? currentTool->Number() : -1;
+}
+
+// Get the current axes used as the specified axis
+AxesBitmap MovementState::GetCurrentAxisMapping(unsigned int axis) const noexcept
+{
+ return Tool::GetAxisMapping(currentTool, axis);
+}
+
+// Get the current axes used as X axis
+AxesBitmap MovementState::GetCurrentXAxes() const noexcept
+{
+ return Tool::GetXAxes(currentTool);
+}
+
+// Get the current axes used as Y axis
+AxesBitmap MovementState::GetCurrentYAxes() const noexcept
+{
+ return Tool::GetYAxes(currentTool);
+}
+
+// Get an axis offset of the current tool
+float MovementState::GetCurrentToolOffset(size_t axis) const noexcept
+{
+ return (currentTool == nullptr) ? 0.0 : currentTool->GetOffset(axis);
+}
+
+// We are currently printing, but we must now stop because the current object is cancelled
+void MovementState::StopPrinting(GCodeBuffer& gb) noexcept
+{
+ currentObjectCancelled = true;
+}
+
+// We are currently not printing because the current object was cancelled, but now we need to print again
+void MovementState::ResumePrinting(GCodeBuffer& gb) noexcept
+{
+ currentObjectCancelled = false;
+ printingJustResumed = true;
+ reprap.GetGCodes().SavePosition(gb, ResumeObjectRestorePointNumber); // save the position we should be at for the start of the next move
+ if (GetCurrentToolNumber() != newToolNumber) // if the wrong tool is loaded
+ {
+ reprap.GetGCodes().StartToolChange(gb, DefaultToolChangeParam);
+ }
+}
+
+void MovementState::InitObjectCancellation() noexcept
+{
+ currentObjectNumber = -1;
+ currentObjectCancelled = printingJustResumed = false;
+}
+
#if SUPPORT_ASYNC_MOVES
void AsyncMove::SetDefaults() noexcept
diff --git a/src/Movement/RawMove.h b/src/Movement/RawMove.h
index 7bccf816..adec7b08 100644
--- a/src/Movement/RawMove.h
+++ b/src/Movement/RawMove.h
@@ -8,20 +8,25 @@
#ifndef SRC_GCODES_RAWMOVE_H_
#define SRC_GCODES_RAWMOVE_H_
-#include "RepRapFirmware.h"
+#include <RepRapFirmware.h>
+#include <GCodes/RestorePoint.h>
-// Details of a move that are passed from GCodes to Move
+// Details of a move that are copied from GCodes to Move
struct RawMove
{
float coords[MaxAxesPlusExtruders]; // new positions for the axes, amount of movement for the extruders
float initialUserC0, initialUserC1; // if this is a segment of an arc move, the user XYZ coordinates at the start
float feedRate; // feed rate of this move
- float virtualExtruderPosition; // the virtual extruder position at the start of this move, for normal moves
+ float moveStartVirtualExtruderPosition; // the virtual extruder position at the start of this move, for normal moves
FilePosition filePos; // offset in the file being printed at the start of reading this move
float proportionDone; // what proportion of the entire move has been done when this segment is complete
float cosXyAngle; // the cosine of the change in XY angle between the previous move and this move
- const Tool *tool; // which tool (if any) is being used
- uint16_t moveType : 3, // the H parameter from the G0 or G1 command, 0 for a normal move
+ float maxPrintingAcceleration;
+ float maxTravelAcceleration;
+
+ const Tool *movementTool; // which tool (if any) is being used by this move
+
+ uint16_t moveType : 3, // the S parameter from the G0 or G1 command, 0 for a normal move
applyM220M221 : 1, // true if this move is affected by M220 and M221 (this could be moved to ExtendedRawMove)
usePressureAdvance : 1, // true if we want to us extruder pressure advance, if there is any extrusion
canPauseAfter : 1, // true if we can pause just after this move and successfully restart
@@ -30,14 +35,19 @@ struct RawMove
usingStandardFeedrate : 1, // true if this move uses the standard feed rate
checkEndstops : 1, // true if any endstops or the Z probe can terminate the move
reduceAcceleration : 1, // true if Z probing so we should limit the Z acceleration
+ inverseTimeMode : 1, // true if executing the move in inverse time mode
linearAxesMentioned : 1, // true if any linear axes were mentioned in the movement command
rotationalAxesMentioned: 1; // true if any rotational axes were mentioned in the movement command
#if SUPPORT_LASER || SUPPORT_IOBITS
LaserPwmOrIoBits laserPwmOrIoBits; // the laser PWM or port bit settings required
-#else
- uint16_t padding;
+# if !defined(DUET3) && !defined(DUET3MINI)
+ uint16_t padding; // pad to make the length a multiple of 4 bytes
+# endif
+#elif defined(DUET3) || defined(DUET3MINI)
+ uint16_t padding; // pad to make the length a multiple of 4 bytes
#endif
+
// If adding any more fields, keep the total size a multiple of 4 bytes so that we can use our optimised assignment operator
void SetDefaults(size_t firstDriveToZero) noexcept; // set up default values
@@ -57,15 +67,27 @@ enum class SegmentedMoveState : uint8_t
aborted
};
+constexpr size_t PauseRestorePointNumber = 1;
+constexpr size_t ToolChangeRestorePointNumber = 2;
+
+constexpr size_t NumTotalRestorePoints = NumVisibleRestorePoints + 2; // The total number of visible + invisible restore points
+constexpr size_t SimulationRestorePointNumber = NumVisibleRestorePoints;
+constexpr size_t ResumeObjectRestorePointNumber = NumVisibleRestorePoints + 1;
+
// Details of a move that are needed only by GCodes
// CAUTION: segmentsLeft should ONLY be changed from 0 to not 0 by calling NewMoveAvailable()!
struct MovementState : public RawMove
{
+ Tool *currentTool; // the current tool of this movement system
+ AxesBitmap axesAndExtrudersOwned; // axes and extruders that this movement system has moved since the last sync
+
// The current user position now holds the requested user position after applying workplace coordinate offsets.
// So we must subtract the workplace coordinate offsets when we want to display them.
// We have chosen this approach because it allows us to switch workplace coordinates systems or turn off applying workplace offsets without having to update currentUserPosition.
float currentUserPosition[MaxAxes]; // The current position of the axes as commanded by the input gcode, after accounting for workplace offset,
// before accounting for tool offset and Z hop
+ float latestVirtualExtruderPosition; // The virtual extruder position of this movement system after completing pending moves
+ float virtualFanSpeed; // Last speed given in a M106 command with no fan number
float currentZHop; // The amount of Z hop that is currently applied
float initialCoords[MaxAxes]; // the initial positions of the axes
float previousX, previousY; // the initial X and Y coordinates in user space of the previous move
@@ -80,12 +102,73 @@ struct MovementState : public RawMove
float currentAngleSine, currentAngleCosine; // the sine and cosine of the current angle
float arcAngleIncrement; // the amount by which we increment the arc angle in each segment
float angleIncrementSine, angleIncrementCosine; // the sine and cosine of the increment
+ float speedFactor; // speed factor as a fraction (normally 1.0)
unsigned int segmentsTillNextFullCalc; // how may more segments we can do before we need to do the full calculation instead of the quicker one
+ GCodeQueue *codeQueue; // Stores certain codes for deferred execution
+
+ GCodeBuffer *null updateUserPositionGb; // if this is non-null then we need to update the user position from the machine position
+
+ unsigned int segmentsLeftToStartAt;
+ float moveFractionToSkip;
+ float firstSegmentFractionToSkip;
+
+ float restartMoveFractionDone; // how much of the next move was printed before the pause or power failure (from M26)
+ float restartInitialUserC0; // if the print was paused during an arc move, the user X coordinate at the start of that move (from M26)
+ float restartInitialUserC1; // if the print was paused during an arc move, the user Y coordinate at the start of that move (from M26)
+
+ RestorePoint restorePoints[NumTotalRestorePoints];
+ RestorePoint& pauseRestorePoint = restorePoints[PauseRestorePointNumber]; // The position and feed rate when we paused the print
+ RestorePoint& toolChangeRestorePoint = restorePoints[ToolChangeRestorePointNumber]; // The position and feed rate when we freed a tool
+ RestorePoint& simulationRestorePoint = restorePoints[SimulationRestorePointNumber]; // The position and feed rate when we started simulating
+ RestorePoint& resumeObjectRestorePoint = restorePoints[ResumeObjectRestorePointNumber]; // The position and feed rate when we resumed printing objects
+
+#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE || HAS_EMBEDDED_FILES
+ FilePosition fileOffsetToPrint; // The offset to print from
+#endif
+
+ // Tool change. These variables can be global because movement is locked while doing a tool change, so only one per movement system can take place at a time.
+ int16_t newToolNumber; // the tool number we are switching to, or the tool number we were supposed to switch to but didn't because the current object has been cancelled
+ int16_t previousToolNumber; // the tool number we were using before the last tool change, or -1 if we weren't using a tool
+ uint8_t toolChangeParam;
+
bool doingArcMove; // true if we are doing an arc move
bool xyPlane; // true if the G17/G18/G19 selected plane of the arc move is XY in the original user coordinates
SegmentedMoveState segMoveState;
+ bool pausedInMacro; // if we are paused then this is true if we paused while fileGCode was executing a macro
+
+ // Object cancellation variables
+ int currentObjectNumber; // the current object number, or a negative value if it isn't an object
+ bool currentObjectCancelled; // true if the current object should not be printed
+ bool printingJustResumed; // true if we have just restarted printing
float GetProportionDone() const noexcept; // get the proportion of this whole move that has been completed, based on segmentsLeft and totalSegments
+ void Reset() noexcept;
+ void ChangeExtrusionFactor(unsigned int extruder, float multiplier) noexcept; // change the extrusion factor of an extruder
+ const RestorePoint& GetRestorePoint(size_t n) const pre(n < NumTotalRestorePoints) { return restorePoints[n]; }
+ void ClearMove() noexcept;
+ void SavePosition(unsigned int restorePointNumber, size_t numAxes, float p_feedRate, FilePosition p_filePos) noexcept
+ pre(restorePointNumber < NumTotalRestorePoints);
+
+ // Tool management
+ void SelectTool(int toolNumber, bool simulating) noexcept;
+ ReadLockedPointer<Tool> GetLockedCurrentTool() const noexcept;
+ ReadLockedPointer<Tool> GetLockedCurrentOrDefaultTool() const noexcept;
+ int GetCurrentToolNumber() const noexcept;
+ void SetPreviousToolNumber() noexcept;
+ AxesBitmap GetCurrentXAxes() const noexcept; // Get the current axes used as X axes
+ AxesBitmap GetCurrentYAxes() const noexcept; // Get the current axes used as Y axes
+ AxesBitmap GetCurrentAxisMapping(unsigned int axis) const noexcept;
+ float GetCurrentToolOffset(size_t axis) const noexcept; // Get an axis offset of the current tool
+
+ // Object cancellation support
+ void InitObjectCancellation() noexcept;
+ bool IsCurrentObjectCancelled() const noexcept { return currentObjectCancelled; }
+ bool IsFirstMoveSincePrintingResumed() const noexcept { return printingJustResumed; }
+ void DoneMoveSincePrintingResumed() noexcept { printingJustResumed = false; }
+ void StopPrinting(GCodeBuffer& gb) noexcept;
+ void ResumePrinting(GCodeBuffer& gb) noexcept;
+
+ void Diagnostics(MessageType mtype, unsigned int moveSystemNumber) noexcept;
};
#if SUPPORT_ASYNC_MOVES
diff --git a/src/Movement/StepperDrivers/TMC22xx.cpp b/src/Movement/StepperDrivers/TMC22xx.cpp
index b1dba193..497d1bb3 100644
--- a/src/Movement/StepperDrivers/TMC22xx.cpp
+++ b/src/Movement/StepperDrivers/TMC22xx.cpp
@@ -29,8 +29,16 @@
# error TMC22xx_USE_SLAVEADDR not defined
#endif
-#ifndef TMC22xx_DEFAULT_STEALTHCHOP
-# error TMC22xx_DEFAULT_STEALTHCHOP not defined
+#ifndef HAS_STALL_DETECT
+# error HAS_STALL_DETECT not defined
+#endif
+
+#ifndef SUPPORT_TMC2240
+# define SUPPORT_TMC2240 0
+#endif
+
+#if SUPPORT_TMC2240 && !HAS_STALL_DETECT
+# error Must set HAS_STALL_DETECT with SUPPORT_TMC2240
#endif
#define TMC22xx_SINGLE_UART (TMC22xx_SINGLE_DRIVER || TMC22xx_HAS_MUX || TMC22xx_USE_SLAVEADDR)
@@ -114,22 +122,40 @@ static bool currentMuxState;
// GCONF register (0x00, RW)
constexpr uint8_t REGNUM_GCONF = 0x00;
-constexpr uint32_t GCONF_USE_VREF = 1 << 0; // use external VRef
-constexpr uint32_t GCONF_INT_RSENSE = 1 << 1; // use internal sense resistors
-constexpr uint32_t GCONF_SPREAD_CYCLE = 1 << 2; // use spread cycle mode (else stealthchop mode)
-constexpr uint32_t GCONF_REV_DIR = 1 << 3; // reverse motor direction
-constexpr uint32_t GCONF_INDEX_OTPW = 1 << 4; // INDEX output shows over temperature warning (else it shows first microstep position)
-constexpr uint32_t GCONF_INDEX_PULSE = 1 << 5; // INDEX output shows pulses from internal pulse generator, else as set by GCONF_INDEX_OTPW
-constexpr uint32_t GCONF_UART = 1 << 6; // PDN_UART used for UART interface (else used for power down)
-constexpr uint32_t GCONF_MSTEP_REG = 1 << 7; // microstep resolution set by MSTEP register (else by MS1 and MS2 pins)
-constexpr uint32_t GCONF_MULTISTEP_FILT = 1 << 8; // pulse generation optimised for >750Hz full stepping frequency
-constexpr uint32_t GCONF_TEST_MODE = 1 << 9; // test mode, do not set this bit for normal operation
-
-constexpr uint32_t DefaultGConfReg =
-#if TMC22xx_DEFAULT_STEALTHCHOP
- GCONF_UART | GCONF_MSTEP_REG | GCONF_MULTISTEP_FILT;
-#else
- GCONF_UART | GCONF_MSTEP_REG | GCONF_MULTISTEP_FILT | GCONF_SPREAD_CYCLE;
+
+// Bit assignments for TMC2208/09
+constexpr uint32_t GCONF09_USE_VREF = 1 << 0; // use external VRef
+constexpr uint32_t GCONF09_INT_RSENSE = 1 << 1; // use internal sense resistors
+constexpr uint32_t GCONF09_SPREAD_CYCLE = 1 << 2; // use spread cycle mode (else stealthchop mode)
+constexpr uint32_t GCONF09_REV_DIR = 1 << 3; // reverse motor direction
+constexpr uint32_t GCONF09_INDEX_OTPW = 1 << 4; // INDEX output shows over temperature warning (else it shows first microstep position)
+constexpr uint32_t GCONF09_INDEX_PULSE = 1 << 5; // INDEX output shows pulses from internal pulse generator, else as set by GCONF_INDEX_OTPW
+constexpr uint32_t GCONF09_UART = 1 << 6; // PDN_UART used for UART interface (else used for power down)
+constexpr uint32_t GCONF09_MSTEP_REG = 1 << 7; // microstep resolution set by MSTEP register (else by MS1 and MS2 pins)
+constexpr uint32_t GCONF09_MULTISTEP_FILT = 1 << 8; // pulse generation optimised for >750Hz full stepping frequency
+constexpr uint32_t GCONF09_TEST_MODE = 1 << 9; // test mode, do not set this bit for normal operation
+
+constexpr uint32_t DefaultGConfReg09 = GCONF09_UART | GCONF09_MSTEP_REG | GCONF09_MULTISTEP_FILT | GCONF09_SPREAD_CYCLE;
+
+#if SUPPORT_TMC2240
+// Bit assignments for TMC2240
+constexpr uint32_t GCONF40_FAST_STANDSTILL = 1 << 1; // 0 = 2^20 clocks, 1 = 2^18 clocks
+constexpr uint32_t GCONF40_EN_PWM_MODE = 1 << 2; // 0 = spreadyCycle, 1 = stealthChop 2
+constexpr uint32_t GCONF40_MULTISTEP_FILT_40 = 1 << 3; // pulse generation optimised for >750Hz full stepping frequency
+constexpr uint32_t GCONF40_REV_DIR = 1 << 4; // reverse motor direction
+constexpr uint32_t GCONF40_DIAG0_ERROR = 1 << 5; // DIAG0 active on driver errors
+constexpr uint32_t GCONF40_DIAG0_OTPW = 1 << 6; // DIAG0 active on over temperature warning
+constexpr uint32_t GCONF40_DIAG0_STALL = 1 << 7; // DIAG1 active on stall
+constexpr uint32_t GCONF40_DIAG1_STALL = 1 << 8; // DIAG1 active on stall
+constexpr uint32_t GCONF40_DIAG1_INDEX = 1 << 9; // DIAG1 active on index position (microstep table position 0)
+constexpr uint32_t GCONF40_DIAG1_ONSTATE = 1 << 10; // DIAG1 active when chopper is on
+constexpr uint32_t GCONF40_DIAG0_PUSHPULL = 1 << 12; // DIAG0 push pull output (else open drain)
+constexpr uint32_t GCONF40_DIAG1_PUSHPULL = 1 << 13; // DIAG1 push pull output (else open drain)
+constexpr uint32_t GCONF40_SMALL_HYSTERESIS = 1 << 14; // hysteresis for step frequency comparison is 1/32 (else 1/16)
+constexpr uint32_t GCONF40_STOP_ENABLE = 1 << 15; // emergency stop when ENCA goes high (motor goes to standstill)
+constexpr uint32_t GCONF40_DIRECT_MODE = 1 << 16; // enable direct coil current control
+
+constexpr uint32_t DefaultGConfReg40 = GCONF40_MULTISTEP_FILT_40 | GCONF40_DIAG0_STALL | GCONF40_DIAG0_PUSHPULL;
#endif
// General configuration and status registers
@@ -140,6 +166,10 @@ constexpr uint32_t GSTAT_RESET = 1 << 0; // driver has been reset since last
constexpr uint32_t GSTAT_DRV_ERR = 1 << 1; // driver has been shut down due to over temp or short circuit
constexpr uint32_t GSTAT_UV_CP = 1 << 2; // undervoltage on charge pump, driver disabled. Not latched so does not need to be cleared.
+#if SUPPORT_TMC2240
+constexpr uint32_t GSTAT40_RESET = 1 << 3; // chip has been reset since the last access to GSTAT, all registers have been restored to default values
+#endif
+
// IFCOUNT register (0x02, RO)
constexpr uint8_t REGNUM_IFCOUNT = 0x02;
constexpr uint32_t IFCOUNT_MASK = 0x000F; // interface transmission counter
@@ -157,7 +187,9 @@ constexpr uint32_t SLAVECONF_SENDDLY_120_BITS = 14 << 8;
constexpr uint32_t DefaultSlaveConfReg = SLAVECONF_SENDDLY_8_BITS; // we don't need any delay between transmission and reception
-// OTP_PROG register (0x04, WO)
+#if 0 // we don't currently use these
+
+// OTP_PROG register (0x04, WO, not TMC2240)
constexpr uint8_t REGNUM_OTP_PROG = 0x04;
constexpr uint32_t OTP_PROG_BIT_SHIFT = 0;
constexpr uint32_t OTP_PROG_BIT_MASK = 7 << OTP_PROG_BIT_SHIFT;
@@ -165,7 +197,7 @@ constexpr uint32_t OTP_PROG_BYTE_SHIFT = 4;
constexpr uint32_t OTP_PROG_BYTE_MASK = 3 << OTP_PROG_BYTE_SHIFT;
constexpr uint32_t OTP_PROG_MAGIC = 0xBD << 8;
-// OTP_READ register (0x05, RO)
+// OTP_READ register (0x05, RO, not TMC2240)
constexpr uint8_t REGNUM_OTP_READ = 0x05;
constexpr uint32_t OTP_READ_BYTE0_SHIFT = 0;
constexpr uint32_t OTP_READ_BYTE0_MASK = 0xFF << OTP_READ_BYTE0_SHIFT;
@@ -174,8 +206,9 @@ constexpr uint32_t OTP_READ_BYTE1_MASK = 0xFF << OTP_READ_BYTE1_SHIFT;
constexpr uint32_t OTP_READ_BYTE2_SHIFT = 16;
constexpr uint32_t OTP_READ_BYTE2_MASK = 0xFF << OTP_READ_BYTE2_SHIFT;
-// IOIN register (0x06, RO)
-constexpr uint8_t REGNUM_IOIN = 0x06;
+// IOIN register for TMC22xx but not TMC2240 (0x06, RO)
+constexpr uint8_t REGNUM_IOIN_22xx = 0x06;
+
constexpr uint32_t IOIN_220x_ENN = 1 << 0;
constexpr uint32_t IOIN_222x_PDN_UART = 1 << 1;
constexpr uint32_t IOIN_220x_MS1 = 1 << 2;
@@ -198,6 +231,9 @@ constexpr uint32_t IOIN_VERSION_MASK = 0xFF << IOIN_VERSION_SHIFT;
constexpr uint32_t IOIN_VERSION_2208_2224 = 0x20; // version for TMC2208/2224
constexpr uint32_t IOIN_VERSION_2209 = 0x21; // version for TMC2209
+// IOIN register for TMC2240 (0x04, RO)
+constexpr uint8_t REGNUM_IOIN_2240 = 0x04;
+
// FACTORY_CONF register (0x07, RW)
constexpr uint8_t REGNUM_FACTORY_CONF = 0x07;
constexpr uint32_t FACTORY_CONF_FCLKTRIM_SHIFT = 0;
@@ -209,27 +245,67 @@ constexpr uint32_t FACTORY_CONF_OTTRIM_150_120 = 0x01 << FACTORY_CONF_OTTRIM_SHI
constexpr uint32_t FACTORY_CONF_OTTRIM_150_143 = 0x02 << FACTORY_CONF_OTTRIM_SHIFT;
constexpr uint32_t FACTORY_CONF_OTTRIM_157_143 = 0x03 << FACTORY_CONF_OTTRIM_SHIFT;
+#endif
+
+#if SUPPORT_TMC2240
+
+// DRV_CONF register (TMC2240 only)
+constexpr uint8_t REGNUM_DRV_CONF40 = 0x0A;
+
+constexpr unsigned int DRV_CONF40_CURRENT_RANGE_SHIFT = 0;
+constexpr uint32_t DRV_CONF40_CURRENT_RANGE_MASK = 0x03; // 0 = 100V/us, 1 = 200V/us, 2 = 400V/us, 3 - 800V/us
+constexpr unsigned int DRV_CONF40_SLOPE_CONTROL_SHIFT = 4;
+constexpr uint32_t DRV_CONF40_SLOPE_CONTROL_MASK = 0x03 << DRV_CONF40_SLOPE_CONTROL_SHIFT; // 0 = 1A, 1 = 2A, 2 = 3A, 3 = 3A peak current
+
+// GLOBAL_SCALER register (TMC2240 only)
+constexpr uint8_t REGNUM_GLOVAL_SCALER40 = 0x0B;
+
+constexpr uint32_t GLOBAL_SCALER40_MASK = 0xFF; // maximum current gets multiplied by this and divided by 255. Must be >= 32; > 128 recommended.
+
+#endif
+
// Velocity dependent control registers
// IHOLD_IRUN register (WO)
constexpr uint8_t REGNUM_IHOLDIRUN = 0x10;
-constexpr uint32_t IHOLDIRUN_IHOLD_SHIFT = 0; // standstill current
+constexpr unsigned int IHOLDIRUN_IHOLD_SHIFT = 0; // standstill current
constexpr uint32_t IHOLDIRUN_IHOLD_MASK = 0x1F << IHOLDIRUN_IHOLD_SHIFT;
-constexpr uint32_t IHOLDIRUN_IRUN_SHIFT = 8;
+constexpr unsigned int IHOLDIRUN_IRUN_SHIFT = 8;
constexpr uint32_t IHOLDIRUN_IRUN_MASK = 0x1F << IHOLDIRUN_IRUN_SHIFT;
-constexpr uint32_t IHOLDIRUN_IHOLDDELAY_SHIFT = 16;
+constexpr unsigned int IHOLDIRUN_IHOLDDELAY_SHIFT = 16;
constexpr uint32_t IHOLDIRUN_IHOLDDELAY_MASK = 0x0F << IHOLDIRUN_IHOLDDELAY_SHIFT;
+#if SUPPORT_TMC2240
+constexpr unsigned int IHOLDIRUN40_IRUNDELAY_SHIFT = 24;
+constexpr uint32_t IHOLDIRUN40_IRUNDELAY_MASK = 0x0F << IHOLDIRUN40_IRUNDELAY_SHIFT;
+#endif
+
constexpr uint32_t DefaultIholdIrunReg = (0 << IHOLDIRUN_IHOLD_SHIFT) | (0 << IHOLDIRUN_IRUN_SHIFT) | (2 << IHOLDIRUN_IHOLDDELAY_SHIFT);
// approx. 0.5 sec motor current reduction to low power
constexpr uint8_t REGNUM_TPOWER_DOWN = 0x11; // wo, 8 bits, sets delay from standstill detection to motor current reduction
constexpr uint8_t REGNUM_TSTEP = 0x12; // ro, 20 bits, measured time between two 1/256 microsteps, in clocks
constexpr uint8_t REGNUM_TPWMTHRS = 0x13; // wo, 20 bits, upper velocity for StealthChop mode
+
+#if SUPPORT_TMC2240
+constexpr uint8_t REGNUM_THIGH40 = 0x15;
+constexpr uint32_t THIGH_MASK = 0x00FFFFFF; // when the step interval is below this, TMC2240 is forced into spreadCycle mode
+#endif
+
constexpr uint8_t REGNUM_VACTUAL = 0x22; // wo, 24 bits signed, sets motor velocity for continuous rotation
-// Stallguard registers (TMC2209 only)
+// Stallguard registers (TMC2209 and TMC2240 only)
constexpr uint8_t REGNUM_TCOOLTHRS = 0x14; // wo, 20-bit lower threshold velocity. CoolStep and the StallGuard DIAG output are enabled above this speed.
+
+#if SUPPORT_TMC2240
+constexpr uint8_t REGNUM_DIRECT_MODE = 0x2D;
+constexpr uint8_t REGNUM_ENCMODE = 0x38;
+constexpr uint8_t REGNUM_X_ENC = 0x39;
+constexpr uint8_t REGNUM_ENC_CONST = 0x3A;
+constexpr uint8_t REGNUM_ENC_STATUS = 0x3B;
+constexpr uint8_t REGNUM_ENC_LATCH = 0x3C;
+#endif
+
constexpr uint8_t REGNUM_SGTHRS = 0x40; // w0, 8-bit stall detection threshold. Stall is signalled when SG_RESULT <= SGTHRS * 2.
constexpr uint8_t REGNUM_SG_RESULT = 0x41; // 10-bit StallGard result, read-only. Bits 0 and 9 are always 0.
constexpr uint8_t REGNUM_COOLCONF = 0x42; // 16-bit CoolStep control
@@ -252,6 +328,28 @@ constexpr uint32_t COOLCONF_SEDN_MASK = 0x0003 << COOLCONF_SEDN_SHIFT;
constexpr unsigned int COOLCONF_SEIMIN_SHIFT = 15;
constexpr uint32_t COOLCONF_SEIMIN_MASK = 0x0001 << COOLCONF_SEIMIN_SHIFT;
+#if SUPPORT_TMC2240
+constexpr uint8_t REGNUM_ADC_VSUPPLY_AIN = 0x50;
+constexpr unsigned int ADC_VSUPPLY_SHIFT = 0;
+constexpr uint32_t ADC_VSUPPLY_MASK = 0x01FFF << ADC_VSUPPLY_SHIFT; // ADC voltage reading via low pass filter
+constexpr unsigned int ADC_AIN_SHIFT = 16;
+constexpr uint32_t ADC_AIN_MASK = 0x01FFF << ADC_AIN_SHIFT; // ADC AIN reading
+
+constexpr uint8_t REGNUM_ADC_TEMP = 0x51;
+constexpr unsigned int ADC_TEMP_SHIFT = 0;
+constexpr uint32_t ADC_TEMP_MASK = 0x01FFF << ADC_TEMP_SHIFT; // ADC temperature reading
+
+constexpr uint8_t REGNUM_OTW_OV_VTH = 0x52;
+constexpr unsigned int OVERVOLTAGE_VTH_SHIFT = 0;
+constexpr uint32_t OVERVOLTAGE_VTH_MASK = 0x01FFF << OVERVOLTAGE_VTH_SHIFT; // ADC over-voltage threshold, default 36V
+constexpr unsigned int OVERTEMPPREWARNING_VTH_SHIFT = 16;
+constexpr uint32_t OVERTEMPPREWARNING_VTH_MASK = 0x01FFF << OVERTEMPPREWARNING_VTH_SHIFT; // ADC over-temperature warning threshold, default 0x0B92 = 120C
+
+constexpr uint8_t REGNUM_MSLUT_0 = 0x60; // first of 8 lookup table registers
+constexpr uint8_t REGNUM_MSLUTSEL = 0x68;
+constexpr uint8_t REGNUM_MSLUTSTART = 0x69;
+#endif
+
// Sequencer registers (read only)
constexpr uint8_t REGNUM_MSCNT = 0x6A;
constexpr uint8_t REGNUM_MSCURACT = 0x6B;
@@ -284,28 +382,43 @@ constexpr uint32_t ChopConf256mstep = DefaultChopConfReg; // the default uses x2
// DRV_STATUS register
constexpr uint8_t REGNUM_DRV_STATUS = 0x6F;
-constexpr uint32_t TMC_RR_OT = 1u << 1; // over temperature shutdown
-constexpr uint32_t TMC_RR_OTPW = 1u << 0; // over temperature warning
-constexpr uint32_t TMC_RR_S2G = 15u << 2; // short to ground counter (4 bits)
-constexpr uint32_t TMC_RR_OLA = 1u << 6; // open load A
-constexpr uint32_t TMC_RR_OLB = 1u << 7; // open load B
-constexpr uint32_t TMC_RR_STST = 1u << 31; // standstill detected
-constexpr uint32_t TMC_RR_OPW_120 = 1u << 8; // temperature threshold exceeded
-constexpr uint32_t TMC_RR_OPW_143 = 1u << 9; // temperature threshold exceeded
-constexpr uint32_t TMC_RR_OPW_150 = 1u << 10; // temperature threshold exceeded
-constexpr uint32_t TMC_RR_OPW_157 = 1u << 11; // temperature threshold exceeded
-constexpr uint32_t TMC_RR_TEMPBITS = 15u << 8; // all temperature threshold bits
-
-constexpr uint32_t TMC_RR_RESERVED = (15u << 12) | (0x01FF << 21); // reserved bits
-constexpr uint32_t TMC_RR_SG = 1u << 12; // this is a reserved bit, which we use to signal a stall
-
-constexpr unsigned int TMC_RR_STST_BIT_POS = 31;
-constexpr unsigned int TMC_RR_SG_BIT_POS = 12;
+
+// Values for TMC2208/2224 and TMC2209
+constexpr uint32_t TMC_RR_OT_2209 = 1u << 1; // over temperature shutdown
+constexpr uint32_t TMC_RR_OTPW_2209 = 1u << 0; // over temperature warning
+constexpr uint32_t TMC_RR_S2G_2209 = 3u << 2; // short to ground indicators (2 bits)
+constexpr uint32_t TMC_RR_S2VS_2209 = 3u << 4; // short to Vs indicators (2 bits)
+constexpr uint32_t TMC_RR_OLA_2209 = 1u << 6; // open load A
+constexpr uint32_t TMC_RR_OLB_2209 = 1u << 7; // open load B
+constexpr uint32_t TMC_RR_STST = 1u << 31; // standstill detected (same bit on 2209 and 2240)
+constexpr uint32_t TMC_RR_OPW_2209_120 = 1u << 8; // temperature threshold exceeded
+constexpr uint32_t TMC_RR_OPW_2209_143 = 1u << 9; // temperature threshold exceeded
+constexpr uint32_t TMC_RR_OPW_2209_150 = 1u << 10; // temperature threshold exceeded
+constexpr uint32_t TMC_RR_OPW_2209_157 = 1u << 11; // temperature threshold exceeded
+constexpr uint32_t TMC_RR_TEMPBITS_2209 = 15u << 8; // all temperature threshold bits
+constexpr uint32_t TMC_RR_RESERVED_2209 = (15u << 12) | (0x01FF << 21); // reserved bits
+
+// Values for TMC2240
+constexpr uint32_t TMC_RR_SGRESULT_MASK_2240 = 1023; // StallGuard result
+constexpr uint32_t TMC_RR_S2VS_2240 = 3u << 12; // short to Vs indicators (2 bits)
+constexpr uint32_t TMC_RR_OT_2240 = 1u << 25; // over temperature shutdown
+constexpr uint32_t TMC_RR_OTPW_2240 = 1u << 26; // over temperature warning
+constexpr uint32_t TMC_RR_S2G_2240 = 3u << 27; // short to ground indicators (2 bits)
+constexpr uint32_t TMC_RR_OLA_2240 = 1u << 29; // open load A
+constexpr uint32_t TMC_RR_OLB_2240 = 1u << 30; // open load B
+
+constexpr unsigned int TMC_RR_S2VS_BITS_POS_2240 = 12;
+constexpr unsigned int TMC_RR_OT_BIT_POS_2240 = 25;
+constexpr unsigned int TMC_RR_OTPW_BIT_POS_2240 = 26;
+constexpr unsigned int TMC_RR_S2G_BITS_POS_2240 = 27;
+constexpr unsigned int TMC_RR_OPENLOAD_BITS_POS_2240 = 29;
+
+constexpr unsigned int TMC_RR_STST_BIT_POS = 31; // same position for 2209 and 2240
// PWMCONF register
constexpr uint8_t REGNUM_PWMCONF = 0x70;
-constexpr uint32_t DefaultPwmConfReg = 0xC10D0024; // this is the reset default - try it until we find something better
+constexpr uint32_t DefaultPwmConfReg = 0xC10D0024; // this is the reset default - try it until we find something better
constexpr uint8_t REGNUM_PWM_SCALE = 0x71;
constexpr uint8_t REGNUM_PWM_AUTO = 0x72;
@@ -422,6 +535,9 @@ public:
#if HAS_STALL_DETECT
, Pin p_diagPin
#endif
+#if SUPPORT_TMC2240
+ , bool p_isTmc2240
+#endif
) noexcept;
void SetAxisNumber(size_t p_axisNumber) noexcept;
uint32_t GetAxisNumber() const noexcept { return axisNumber; }
@@ -439,7 +555,6 @@ public:
#endif
void AppendDriverStatus(const StringRef& reply) noexcept;
StandardDriverStatus GetStatus(bool accumulated, bool clearAccumulated) noexcept;
- uint8_t GetDriverNumber() const noexcept { return driverNumber; }
bool UpdatePending() const noexcept;
#if TMC22xx_HAS_ENABLE_PINS
bool UsesGlobalEnable() const noexcept { return enablePin == NoPin; }
@@ -492,11 +607,14 @@ public:
static uint32_t transferStartedTime;
void UartTmcHandler() noexcept; // core of the ISR for this driver
+
private:
+ bool IsStealthChop() const noexcept;
bool SetChopConf(uint32_t newVal) noexcept;
void UpdateRegister(size_t regIndex, uint32_t regVal) noexcept;
void UpdateCurrent() noexcept;
void UpdateMaxOpenLoadStepInterval() noexcept;
+
#if HAS_STALL_DETECT
void ResetLoadRegisters() noexcept
{
@@ -504,6 +622,17 @@ private:
}
#endif
+#if TMC22xx_USE_SLAVEADDR
+ uint8_t GetSlaveAddr() const noexcept
+ {
+# if SUPPORT_TMC2240
+ return driverNumber & ((isTmc2240) ? 7u : 3u);
+# else
+ return driverNumber & 3u;
+# endif
+ }
+#endif
+
#if TMC22xx_HAS_MUX
void SetUartMux() noexcept;
#endif
@@ -515,7 +644,9 @@ private:
static void SetupDMARead(uint8_t regnum) noexcept SPEED_CRITICAL; // set up the DMAC to receive a register
#endif
-#if HAS_STALL_DETECT
+#if SUPPORT_TMC2240
+ static constexpr unsigned int NumWriteRegisters = 10; // the number of registers that we write to on a TMC2240
+#elif HAS_STALL_DETECT
static constexpr unsigned int NumWriteRegisters = 9; // the number of registers that we write to on a TMC2209
#else
static constexpr unsigned int NumWriteRegisters = 6; // the number of registers that we write to on a TMC2208/2224
@@ -534,25 +665,30 @@ private:
static constexpr unsigned int WriteSgthrs = 7; // stallguard threshold
static constexpr unsigned int WriteCoolconf = 8; // coolstep configuration
#endif
+#if SUPPORT_TMC2240
+ static constexpr unsigned int WriteDrvConf = 9;
+#endif
static constexpr unsigned int WriteSpecial = NumWriteRegisters;
#if HAS_STALL_DETECT
- static constexpr unsigned int NumReadRegisters = 8; // the number of registers that we read from on a TMC2209
+ static constexpr unsigned int NumReadRegisters = 7; // the number of registers that we read from on a TMC2209
#else
- static constexpr unsigned int NumReadRegisters = 7; // the number of registers that we read from on a TMC2208/2224
+ static constexpr unsigned int NumReadRegisters = 6; // the number of registers that we read from on a TMC2208/2224
#endif
static const uint8_t ReadRegNumbers[NumReadRegisters]; // the register numbers that we read from
// Read register numbers, in same order as ReadRegNumbers
- static constexpr unsigned int ReadIoIn = 0; // includes the version which we use to distinguish TMC2209 from 2208/2224
- static constexpr unsigned int ReadGStat = 1; // global status
- static constexpr unsigned int ReadDrvStat = 2; // drive status
- static constexpr unsigned int ReadMsCnt = 3; // microstep counter
- static constexpr unsigned int ReadChopConf = 4; // chopper control register - we read it to detect the VSENSE bit getting cleared
- static constexpr unsigned int ReadPwmScale = 5; // PWM scaling
- static constexpr unsigned int ReadPwmAuto = 6; // PWM scaling
+ static constexpr unsigned int ReadGStat = 0; // global status
+ static constexpr unsigned int ReadDrvStat = 1; // drive status
+ static constexpr unsigned int ReadMsCnt = 2; // microstep counter
+ static constexpr unsigned int ReadChopConf = 3; // chopper control register - we read it to detect the VSENSE bit getting cleared
+ static constexpr unsigned int ReadPwmScale = 4; // PWM scaling
+ static constexpr unsigned int ReadPwmAuto = 5; // PWM scaling
#if HAS_STALL_DETECT
- static constexpr unsigned int ReadSgResult = 7; // stallguard result, TMC2209 only
+ static constexpr unsigned int ReadSgResult = 6; // stallguard result, TMC2209 only
+#endif
+#if SUPPORT_TMC2240
+ static constexpr unsigned int ReadAdcTemp = 6; // driver temperature, TMC2240 only
#endif
static constexpr unsigned int ReadSpecial = NumReadRegisters;
@@ -622,6 +758,9 @@ private:
volatile uint8_t specialReadRegisterNumber; // the special register number we are reading
volatile uint8_t specialWriteRegisterNumber; // the special register number we are writing
bool enabled; // true if driver is enabled
+#if SUPPORT_TMC2240
+ bool isTmc2240; // true for TMC2240, false for TMC2208/09/24
+#endif
#if RESET_MICROSTEP_COUNTERS_AT_INIT
bool hadStepFailure;
#endif
@@ -685,13 +824,15 @@ constexpr uint8_t TmcDriverState::WriteRegNumbers[NumWriteRegisters] =
// The rest are on TMC2209 only
REGNUM_TCOOLTHRS,
REGNUM_SGTHRS,
- REGNUM_COOLCONF
+ REGNUM_COOLCONF,
+#endif
+#if SUPPORT_TMC2240
+ REGNUM_DRV_CONF40
#endif
};
constexpr uint8_t TmcDriverState::ReadRegNumbers[NumReadRegisters] =
{
- REGNUM_IOIN, // tells us whether we have a TMC2208/24 or a TMC2209
REGNUM_GSTAT,
REGNUM_DRV_STATUS,
REGNUM_MSCNT,
@@ -699,7 +840,7 @@ constexpr uint8_t TmcDriverState::ReadRegNumbers[NumReadRegisters] =
REGNUM_PWM_SCALE,
REGNUM_PWM_AUTO,
#if HAS_STALL_DETECT
- REGNUM_SG_RESULT // TMC2209 only
+ REGNUM_SG_RESULT // TMC2209 only - for TMC2240 we read REGNUM_ADC_TEMP instead
#endif
};
@@ -725,7 +866,7 @@ inline void TmcDriverState::SetupDMASend(uint8_t regNum, uint32_t regVal) noexce
#endif
#if TMC22xx_USE_SLAVEADDR
- const uint8_t slaveAddress = driverNumber & 3u;
+ const uint8_t slaveAddress = GetSlaveAddr();
sendData[SendDataSlaveAddressIndex0] = slaveAddress;
sendData[SendDataSlaveAddressIndex1] = slaveAddress;
uint8_t crc = initialSendCRC;
@@ -814,7 +955,7 @@ inline void TmcDriverState::SetupDMARead(uint8_t regNum) noexcept
#endif
#if TMC22xx_USE_SLAVEADDR
- sendData[SendDataSlaveAddressIndex0] = driverNumber & 3u;
+ sendData[SendDataSlaveAddressIndex0] = GetSlaveAddr();
uint8_t crc = initialSendCRC;
#else
sendData[SendDataSlaveAddressIndex0] = 0;
@@ -859,15 +1000,22 @@ inline void TmcDriverState::SetupDMARead(uint8_t regNum) noexcept
#endif
}
+// Return true if this driver is operating in stealthChop mode
+bool TmcDriverState::IsStealthChop() const noexcept
+{
+ const uint32_t gconf = writeRegisters[WriteGConf];
+ return
+#if SUPPORT_TMC2240
+ (isTmc2240) ? (gconf & GCONF40_EN_PWM_MODE) != 0 :
+#endif
+ (gconf & GCONF09_SPREAD_CYCLE) == 0;
+}
+
// Update the maximum step pulse interval at which we consider open load detection to be reliable
void TmcDriverState::UpdateMaxOpenLoadStepInterval() noexcept
{
const uint32_t defaultMaxInterval = StepClockRate/MinimumOpenLoadFullStepsPerSec;
- if ((writeRegisters[WriteGConf] & GCONF_SPREAD_CYCLE) != 0)
- {
- maxOpenLoadStepInterval = defaultMaxInterval;
- }
- else
+ if (IsStealthChop())
{
// In stealthchop mode open load detection in unreliable, so disable it below the speed at which we switch to spreadCycle
const uint32_t tpwmthrs = writeRegisters[WriteTpwmthrs] & 0x000FFFFF;
@@ -878,6 +1026,10 @@ void TmcDriverState::UpdateMaxOpenLoadStepInterval() noexcept
const uint32_t fullStepClocks = tpwmthrs * conversionFactor;
maxOpenLoadStepInterval = min<uint32_t>(fullStepClocks, defaultMaxInterval);
}
+ else
+ {
+ maxOpenLoadStepInterval = defaultMaxInterval;
+ }
}
// Set a register value and flag it for updating
@@ -918,11 +1070,17 @@ void TmcDriverState::Init(uint32_t p_driverNumber
#if HAS_STALL_DETECT
, Pin p_diagPin
#endif
+#if SUPPORT_TMC2240
+ , bool p_isTmc2240
+#endif
) noexcept
pre(!driversPowered)
{
driverNumber = p_driverNumber;
axisNumber = p_driverNumber; // assume straight-through axis mapping initially
+#if SUPPORT_TMC2240
+ isTmc2240 = p_isTmc2240;
+#endif
#if TMC22xx_HAS_ENABLE_PINS
enablePin = p_enablePin; // this is NoPin for the built-in drivers
IoPort::SetPinMode(p_enablePin, OUTPUT_HIGH);
@@ -943,7 +1101,7 @@ pre(!driversPowered)
#endif
#if TMC22xx_USE_SLAVEADDR
- initialSendCRC = CRCAddByte(InitialByteCRC, driverNumber & 3u); // CRC of the first 2 bytes of any transmission
+ initialSendCRC = CRCAddByte(InitialByteCRC, GetSlaveAddr()); // CRC of the first 2 bytes of any transmission
readIfCountCRC =
# if USE_FAST_CRC
Reflect(CRCAddByte(initialSendCRC, REGNUM_IFCOUNT));
@@ -959,7 +1117,11 @@ pre(!driversPowered)
specialReadRegisterNumber = specialWriteRegisterNumber = 0xFF;
motorCurrent = 0.0;
standstillCurrentFraction = (uint8_t)min<uint32_t>((DefaultStandstillCurrentPercent * 256)/100, 255);
- UpdateRegister(WriteGConf, DefaultGConfReg);
+ UpdateRegister(WriteGConf,
+#if SUPPORT_TMC2240
+ (isTmc2240) ? DefaultGConfReg40 :
+#endif
+ DefaultGConfReg09);
UpdateRegister(WriteSlaveConf, DefaultSlaveConfReg);
configuredChopConfReg = DefaultChopConfReg;
SetMicrostepping(DefaultMicrosteppingShift, DefaultInterpolation); // this also updates the chopper control register
@@ -971,6 +1133,12 @@ pre(!driversPowered)
SetStallMinimumStepsPerSecond(DefaultMinimumStepsPerSecond);
UpdateRegister(WriteCoolconf, 0); // coolStep disabled
#endif
+#if SUPPORT_TMC2240
+ if (isTmc2240)
+ {
+ UpdateRegister(WriteDrvConf, 0x02); // set range to maximum
+ }
+#endif
for (size_t i = 0; i < NumReadRegisters; ++i)
{
@@ -1188,11 +1356,29 @@ bool TmcDriverState::SetDriverMode(unsigned int mode) noexcept
switch (mode)
{
case (unsigned int)DriverMode::spreadCycle:
- UpdateRegister(WriteGConf, writeRegisters[WriteGConf] | GCONF_SPREAD_CYCLE);
+#if SUPPORT_TMC2240
+ if (isTmc2240)
+ {
+ UpdateRegister(WriteGConf, writeRegisters[WriteGConf] & ~GCONF40_EN_PWM_MODE);
+ }
+ else
+#endif
+ {
+ UpdateRegister(WriteGConf, writeRegisters[WriteGConf] | GCONF09_SPREAD_CYCLE);
+ }
return true;
case (unsigned int)DriverMode::stealthChop:
- UpdateRegister(WriteGConf, writeRegisters[WriteGConf] & ~GCONF_SPREAD_CYCLE);
+#if SUPPORT_TMC2240
+ if (isTmc2240)
+ {
+ UpdateRegister(WriteGConf, writeRegisters[WriteGConf] | GCONF40_EN_PWM_MODE);
+ }
+ else
+#endif
+ {
+ UpdateRegister(WriteGConf, writeRegisters[WriteGConf] & ~GCONF09_SPREAD_CYCLE);
+ }
return true;
default:
@@ -1203,19 +1389,29 @@ bool TmcDriverState::SetDriverMode(unsigned int mode) noexcept
// Get the driver mode
DriverMode TmcDriverState::GetDriverMode() const noexcept
{
- return ((writeRegisters[WriteGConf] & GCONF_SPREAD_CYCLE) != 0) ? DriverMode::spreadCycle : DriverMode::stealthChop;
+ return (IsStealthChop()) ? DriverMode::stealthChop : DriverMode::spreadCycle;
}
// Set the motor current
void TmcDriverState::SetCurrent(float current) noexcept
{
- motorCurrent = constrain<float>(current, 50.0, MaximumMotorCurrent);
+ motorCurrent = constrain<float>(current, 50.0,
+#if SUPPORT_TMC2240
+ (isTmc2240) ? MaximumTmc2240MotorCurrent :
+#endif
+ MaximumMotorCurrent
+ );
UpdateCurrent();
}
void TmcDriverState::UpdateCurrent() noexcept
{
- const float idealIRunCs = DriverCsMultiplier * motorCurrent;
+ const float idealIRunCs = motorCurrent *
+#if SUPPORT_TMC2240
+ ((isTmc2240) ? Tmc2240CsMultiplier : DriverCsMultiplier);
+#else
+ DriverCsMultiplier;
+#endif
const uint32_t iRunCsBits = constrain<uint32_t>((unsigned int)(idealIRunCs + 0.2), 1, 32) - 1;
const float idealIHoldCs = idealIRunCs * standstillCurrentFraction * (1.0/256.0);
const uint32_t iHoldCsBits = constrain<uint32_t>((unsigned int)(idealIHoldCs + 0.2), 1, 32) - 1;
@@ -1262,21 +1458,34 @@ StandardDriverStatus TmcDriverState::GetStatus(bool accumulated, bool clearAccum
status = readRegisters[ReadDrvStat];
if (!enabled)
{
- status &= ~(TMC_RR_OLA | TMC_RR_OLB);
+ status &=
+#if SUPPORT_TMC2240
+ (isTmc2240) ? ~(TMC_RR_OLA_2240 | TMC_RR_OLB_2240) :
+#endif
+ ~(TMC_RR_OLA_2209 | TMC_RR_OLB_2209);
}
}
-#if HAS_STALL_DETECT
- if (IoPort::ReadPin(diagPin))
+#if SUPPORT_TMC2240
+ if (isTmc2240)
{
- status |= TMC_RR_SG;
+ rslt.all = ExtractBit(status, TMC_RR_OT_BIT_POS_2240, StandardDriverStatus::OtBitPos)
+ | ExtractBit(status, TMC_RR_OTPW_BIT_POS_2240, StandardDriverStatus::OtpwBitPos)
+ | ExtractTwoBits(status, TMC_RR_S2G_BITS_POS_2240, StandardDriverStatus::S2gBitsPos)
+ | ExtractTwoBits(status, TMC_RR_S2VS_BITS_POS_2240, StandardDriverStatus::S2vsBitsPos)
+ | ExtractTwoBits(status, TMC_RR_OPENLOAD_BITS_POS_2240, StandardDriverStatus::OpenLoadBitsPos);
}
+ else
#endif
-
- // The lowest 8 bits of StandardDriverStatus have the same meanings as for the TMC2209 status
- rslt.all = status & 0x000000FF;
+ {
+ // The lowest 8 bits of StandardDriverStatus have the same meanings as for the TMC2209 status
+ rslt.all = status & 0x000000FF;
+ }
rslt.all |= ExtractBit(status, TMC_RR_STST_BIT_POS, StandardDriverStatus::StandstillBitPos); // put the standstill bit in the right place
- rslt.all |= ExtractBit(status, TMC_RR_SG_BIT_POS, StandardDriverStatus::StallBitPos); // put the stall bit in the right place
#if HAS_STALL_DETECT
+ if (IoPort::ReadPin(diagPin))
+ {
+ rslt.stall = true;
+ }
rslt.sgresultMin = minSgLoadRegister;
#endif
}
@@ -1288,7 +1497,7 @@ StandardDriverStatus TmcDriverState::GetStatus(bool accumulated, bool clearAccum
return rslt;
}
-// Append any additional driver status to a string, and reset the min/max load values
+// Append any additional driver status to a string, and reset values as appropriate
void TmcDriverState::AppendDriverStatus(const StringRef& reply) noexcept
{
#if RESET_MICROSTEP_COUNTERS_AT_INIT
@@ -1309,6 +1518,12 @@ void TmcDriverState::AppendDriverStatus(const StringRef& reply) noexcept
}
ResetLoadRegisters();
#endif
+#if SUPPORT_TMC2240
+ if (isTmc2240)
+ {
+ reply.catf(", temp %.1fC", (double)((float)(((readRegisters[ReadAdcTemp] & ADC_TEMP_MASK) >> ADC_TEMP_SHIFT) - 2038)/7.7));
+ }
+#endif
reply.catf(", read errors %u, write errors %u, ifcnt %u, reads %u, writes %u, timeouts %u, DMA errors %u, CC errors %u",
readErrors, writeErrors, lastIfCount, numReads, numWrites, numTimeouts, numDmaErrors, badChopConfErrors);
@@ -1351,7 +1566,11 @@ inline void TmcDriverState::TransferDone() noexcept
}
else if (driversState != DriversState::noPower) // we don't check the CRC, so only accept the result if power is still good
{
- const uint8_t readRegNumber = (registerToRead < NumReadRegisters) ? ReadRegNumbers[registerToRead] : specialReadRegisterNumber;
+ const uint8_t readRegNumber = (registerToRead >= NumReadRegisters) ? specialReadRegisterNumber
+#if SUPPORT_TMC2240
+ : (registerToRead == ReadSgResult && isTmc2240) ? REGNUM_ADC_TEMP // on TMC2240 read ADC_TEMP instead of SGRESULT
+#endif
+ : ReadRegNumbers[registerToRead];
if (sendData[2] == readRegNumber
&& readRegNumber == receiveData[6]
&& receiveData[4] == 0x05
@@ -1370,8 +1589,22 @@ inline void TmcDriverState::TransferDone() noexcept
|| motorCurrent < MinimumOpenLoadMotorCurrent
)
{
- regVal &= ~(TMC_RR_OLA | TMC_RR_OLB); // open load bits are unreliable at standstill and low speeds
+ regVal &=
+#if SUPPORT_TMC2240
+ (isTmc2240) ? ~(TMC_RR_OLA_2240 | TMC_RR_OLB_2240) :
+#endif
+ ~(TMC_RR_OLA_2209 | TMC_RR_OLB_2209); // open load bits are unreliable at standstill and low speeds
}
+#if SUPPORT_TMC2240
+ if (isTmc2240)
+ {
+ const uint16_t sgResult = regVal & TMC_RR_SGRESULT_MASK_2240;
+ if (sgResult < minSgLoadRegister)
+ {
+ minSgLoadRegister = sgResult;
+ }
+ }
+#endif
}
else if (registerToRead == ReadChopConf)
{
@@ -1383,7 +1616,11 @@ inline void TmcDriverState::TransferDone() noexcept
}
}
#if HAS_STALL_DETECT
- else if (registerToRead == ReadSgResult)
+ else if (registerToRead == ReadSgResult
+# if SUPPORT_TMC2240
+ && !isTmc2240
+# endif
+ )
{
const uint16_t sgResult = regVal & SG_RESULT_MASK;
if (sgResult < minSgLoadRegister)
@@ -1507,7 +1744,11 @@ inline void TmcDriverState::StartTransfer() noexcept
uart->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX; // reset transmitter and receiver
#endif
- const uint8_t readRegNumber = (registerToRead < NumReadRegisters) ? ReadRegNumbers[registerToRead] : specialReadRegisterNumber;
+ const uint8_t readRegNumber = (registerToRead >= NumReadRegisters) ? specialReadRegisterNumber
+#if SUPPORT_TMC2240
+ : (registerToRead == ReadSgResult && isTmc2240) ? REGNUM_ADC_TEMP // on TMC2240 read ADC_TEMP instead of SGRESULT
+#endif
+ : ReadRegNumbers[registerToRead];
SetupDMARead(readRegNumber); // set up the DMAC
#if TMC22xx_USES_SERCOM
@@ -1830,6 +2071,8 @@ debugPrintf("Driver %u ok\n", driver);
// It is assumed that the drivers are not powered, so driversPowered(true) must be called after calling this before the motors can be moved.
#if TMC22xx_VARIABLE_NUM_DRIVERS
void SmartDrivers::Init(size_t numTmcDrivers) noexcept
+#elif SUPPORT_TMC2240 && defined(DUET3MINI)
+void SmartDrivers::Init(bool hasTmc2240Expansion) noexcept
#else
void SmartDrivers::Init() noexcept
#endif
@@ -1916,6 +2159,13 @@ void SmartDrivers::Init() noexcept
#if HAS_STALL_DETECT
, DriverDiagPins[drive]
#endif
+#if SUPPORT_TMC2240
+# ifdef DUET3MINI
+ , hasTmc2240Expansion && drive >= 5 // drivers 5 and 6 may be TMC2240
+# else
+ , false
+# endif
+#endif
);
}
diff --git a/src/Movement/StepperDrivers/TMC22xx.h b/src/Movement/StepperDrivers/TMC22xx.h
index 28008602..9e1013ae 100644
--- a/src/Movement/StepperDrivers/TMC22xx.h
+++ b/src/Movement/StepperDrivers/TMC22xx.h
@@ -19,6 +19,8 @@ namespace SmartDrivers
#if TMC22xx_VARIABLE_NUM_DRIVERS
void Init(size_t numTmcDrivers) noexcept
pre(numTmcDrivers <= MaxSmartDrivers);
+#elif SUPPORT_TMC2240 && defined(DUET3MINI)
+ void Init(bool hasTmc2240Expansion) noexcept;
#else
void Init() noexcept;
#endif
diff --git a/src/Networking/ESP8266WiFi/WiFiInterface.cpp b/src/Networking/ESP8266WiFi/WiFiInterface.cpp
index 67eae626..e5f4b2b4 100644
--- a/src/Networking/ESP8266WiFi/WiFiInterface.cpp
+++ b/src/Networking/ESP8266WiFi/WiFiInterface.cpp
@@ -231,6 +231,55 @@ static inline void DisableEspInterrupt() noexcept
detachInterrupt(EspDataReadyPin);
}
+static const char* GetWiFiAuthFriendlyStr(WiFiAuth auth)
+{
+ const char* res = "Unknown";
+
+ switch (auth)
+ {
+ case WiFiAuth::OPEN:
+ res = "Open";
+ break;
+
+ case WiFiAuth::WEP:
+ res = "WEP";
+ break;
+
+ case WiFiAuth::WPA_PSK:
+ res = "WPA-Personal";
+ break;
+
+ case WiFiAuth::WPA2_PSK:
+ res = "WPA2-Personal";
+ break;
+
+ case WiFiAuth::WPA_WPA2_PSK:
+ res = "WPA/WPA2-Personal";
+ break;
+
+ case WiFiAuth::WPA2_ENTERPRISE:
+ res = "WPA2-Enterprise";
+ break;
+
+ case WiFiAuth::WPA3_PSK:
+ res = "WPA3-Personal";
+ break;
+
+ case WiFiAuth::WPA2_WPA3_PSK:
+ res = "WPA2/WPA3-Personal";
+ break;
+
+ case WiFiAuth::WAPI_PSK:
+ res = "WAPI-Personal";
+ break;
+
+ default:
+ break;
+ }
+
+ return res;
+}
+
/*-----------------------------------------------------------------------------------*/
// WiFi interface class
@@ -1066,105 +1115,172 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri
switch (mcode)
{
case 587: // Add WiFi network or list remembered networks
- if (gb.Seen('S'))
+ switch (gb.GetCommandFraction())
{
- WirelessConfigurationData config;
- memset(&config, 0, sizeof(config));
- String<ARRAY_SIZE(config.ssid)> ssid;
- gb.GetQuotedString(ssid.GetRef());
- SafeStrncpy(config.ssid, ssid.c_str(), ARRAY_SIZE(config.ssid));
-
- // Get the password
- gb.MustSee('P');
- {
- String<ARRAY_SIZE(config.password)> password;
- gb.GetQuotedString(password.GetRef());
- if (password.strlen() < 8 && password.strlen() != 0) // WPA2 passwords must be at least 8 characters
+ case -1:
+ case 0:
+ if (gb.Seen('S'))
{
- reply.copy("WiFi password must be at least 8 characters");
- return GCodeResult::error;
- }
- SafeStrncpy(config.password, password.c_str(), ARRAY_SIZE(config.password));
- }
+ WirelessConfigurationData config;
+ memset(&config, 0, sizeof(config));
+ String<ARRAY_SIZE(config.ssid)> ssid;
+ gb.GetQuotedString(ssid.GetRef());
+ SafeStrncpy(config.ssid, ssid.c_str(), ARRAY_SIZE(config.ssid));
+
+ // Get the password
+ gb.MustSee('P');
+ {
+ String<ARRAY_SIZE(config.password)> password;
+ gb.GetQuotedString(password.GetRef());
+ if (password.strlen() < 8 && password.strlen() != 0) // WPA2 passwords must be at least 8 characters
+ {
+ reply.copy("WiFi password must be at least 8 characters");
+ return GCodeResult::error;
+ }
+ SafeStrncpy(config.password, password.c_str(), ARRAY_SIZE(config.password));
+ }
- if (gb.Seen('I'))
- {
- IPAddress temp;
- gb.GetIPAddress(temp);
- config.ip = temp.GetV4LittleEndian();
- }
- if (gb.Seen('J'))
- {
- IPAddress temp;
- gb.GetIPAddress(temp);
- config.gateway = temp.GetV4LittleEndian();
- }
- if (gb.Seen('K'))
- {
- IPAddress temp;
- gb.GetIPAddress(temp);
- config.netmask = temp.GetV4LittleEndian();
- }
+ if (gb.Seen('I'))
+ {
+ IPAddress temp;
+ gb.GetIPAddress(temp);
+ config.ip = temp.GetV4LittleEndian();
+ }
+ if (gb.Seen('J'))
+ {
+ IPAddress temp;
+ gb.GetIPAddress(temp);
+ config.gateway = temp.GetV4LittleEndian();
+ }
+ if (gb.Seen('K'))
+ {
+ IPAddress temp;
+ gb.GetIPAddress(temp);
+ config.netmask = temp.GetV4LittleEndian();
+ }
- const int32_t rslt = SendCommand(NetworkCommand::networkAddSsid, 0, 0, 0, &config, sizeof(config), nullptr, 0);
- if (rslt == ResponseEmpty)
- {
- return GCodeResult::ok;
- }
- else
- {
- reply.printf("Failed to add SSID to remembered list: %s", TranslateWiFiResponse(rslt));
- }
- }
- else
- {
- // List remembered networks
- if (longReply == nullptr && !OutputBuffer::Allocate(longReply))
- {
- return GCodeResult::notFinished; // try again later
- }
+ const int32_t rslt = SendCommand(NetworkCommand::networkAddSsid, 0, 0, 0, &config, sizeof(config), nullptr, 0);
+ if (rslt == ResponseEmpty)
+ {
+ return GCodeResult::ok;
+ }
+ else
+ {
+ reply.printf("Failed to add SSID to remembered list: %s", TranslateWiFiResponse(rslt));
+ }
+ }
+ else
+ {
+ // List remembered networks
+ if (longReply == nullptr && !OutputBuffer::Allocate(longReply))
+ {
+ return GCodeResult::notFinished; // try again later
+ }
- const bool jsonFormat = gb.Seen('F') && gb.GetUIValue() == 1;
+ const bool jsonFormat = gb.Seen('F') && gb.GetUIValue() == 1;
- const size_t declaredBufferLength = (MaxRememberedNetworks + 1) * ReducedWirelessConfigurationDataSize; // enough for all the remembered SSID data
- uint32_t buffer[NumDwords(declaredBufferLength)];
- const int32_t rslt = SendCommand(NetworkCommand::networkRetrieveSsidData, 0, 0, 0, nullptr, 0, buffer, declaredBufferLength);
- if (rslt >= 0)
- {
- longReply->copy((jsonFormat) ? "{\"rememberedNetworks\":[" : "Remembered networks:");
- size_t offset = (jsonFormat) ? 0 : ReducedWirelessConfigurationDataSize; // skip own SSID details unless reporting in JSON format
- bool found = false;
- while (offset + ReducedWirelessConfigurationDataSize <= (size_t)rslt)
- {
- WirelessConfigurationData* const wp = reinterpret_cast<WirelessConfigurationData *>(reinterpret_cast<char*>(buffer) + offset);
- if (wp->ssid[0] != 0 || (offset == 0 && jsonFormat))
+ const size_t declaredBufferLength = (MaxRememberedNetworks + 1) * ReducedWirelessConfigurationDataSize; // enough for all the remembered SSID data
+ uint32_t buffer[NumDwords(declaredBufferLength)];
+ const int32_t rslt = SendCommand(NetworkCommand::networkRetrieveSsidData, 0, 0, 0, nullptr, 0, buffer, declaredBufferLength);
+ if (rslt >= 0)
{
- wp->ssid[ARRAY_UPB(wp->ssid)] = 0;
- if (jsonFormat && found)
+ longReply->copy((jsonFormat) ? "{\"rememberedNetworks\":[" : "Remembered networks:");
+ size_t offset = (jsonFormat) ? 0 : ReducedWirelessConfigurationDataSize; // skip own SSID details unless reporting in JSON format
+ bool found = false;
+ while (offset + ReducedWirelessConfigurationDataSize <= (size_t)rslt)
{
- longReply->cat(',');
+ WirelessConfigurationData* const wp = reinterpret_cast<WirelessConfigurationData *>(reinterpret_cast<char*>(buffer) + offset);
+ if (wp->ssid[0] != 0 || (offset == 0 && jsonFormat))
+ {
+ wp->ssid[ARRAY_UPB(wp->ssid)] = 0;
+ if (jsonFormat && found)
+ {
+ longReply->cat(',');
+ }
+ longReply->catf((jsonFormat)
+ ? "{\"ssid\":\"%.s\",\"ip\":\"%s\",\"gw\":\"%s\",\"mask\":\"%s\"}"
+ : "\n%s IP=%s GW=%s NM=%s",
+ wp->ssid, IP4String(wp->ip).c_str(), IP4String(wp->gateway).c_str(), IP4String(wp->netmask).c_str());
+ found = true;
+ }
+ offset += ReducedWirelessConfigurationDataSize;
}
- longReply->catf((jsonFormat)
- ? "{\"ssid\":\"%.s\",\"ip\":\"%s\",\"gw\":\"%s\",\"mask\":\"%s\"}"
- : "\n%s IP=%s GW=%s NM=%s",
- wp->ssid, IP4String(wp->ip).c_str(), IP4String(wp->gateway).c_str(), IP4String(wp->netmask).c_str());
- found = true;
+
+ if (jsonFormat)
+ {
+ longReply->cat("],\"err\":0}\n");
+ }
+ else if (!found)
+ {
+ longReply->cat(" none");
+ }
+ return GCodeResult::ok;
}
- offset += ReducedWirelessConfigurationDataSize;
+
+ longReply->printf((jsonFormat) ? "{\"rememberedNetworks\":[],\"err\":1,\"errText\":\"%.s\"}" : "Failed to retrieve network list: %s", TranslateWiFiResponse(rslt));
}
+ break;
- if (jsonFormat)
+ case 1:
{
- longReply->cat("],\"err\":0}\n");
+ const int32_t rslt = SendCommand(NetworkCommand::networkStartScan, 0, 0, 0, nullptr, 0, nullptr, 0);
+ if (rslt >= 0) {
+ return GCodeResult::ok;
+ }
+ reply.printf("failed to start scan: %s\n", TranslateWiFiResponse(rslt));
}
- else if (!found)
+ break;
+
+ case 2:
{
- longReply->cat(" none");
+ if (longReply == nullptr && !OutputBuffer::Allocate(longReply))
+ {
+ return GCodeResult::notFinished; // try again later
+ }
+
+ uint32_t buffer[NumDwords(MaxDataLength + 1)];
+ memset(buffer, 0, sizeof(buffer));
+
+ const bool jsonFormat = gb.Seen('F') && gb.GetUIValue() == 1;
+ const int32_t rslt = SendCommand(NetworkCommand::networkGetScanResult, 0, 0, 0, nullptr, 0, buffer, sizeof(buffer));
+
+ if (rslt >= 0) {
+ bool found = false;
+ longReply->copy((jsonFormat) ? "{\"networkScanResults\":[" : "Network Scan Results:");
+ WiFiScanData *data = reinterpret_cast<WiFiScanData*>(buffer);
+
+ for(int i = 0; data[i].ssid[0] != 0; i++) {
+ if (jsonFormat && found)
+ {
+ longReply->cat(',');
+ }
+ longReply->catf((jsonFormat)
+ ? "{\"ssid\":\"%s\",\"rssi\":\"%d\",\"phymode\":\"%s\",\"auth\":\"%s\"}"
+ : "\nssid=%s rssi=%d phymode=%s auth=%s",
+ data[i].ssid,
+ data[i].rssi,
+ data[i].phymode == EspWiFiPhyMode::N ? "n" : data[i].phymode == EspWiFiPhyMode::G ? "g" : "b",
+ GetWiFiAuthFriendlyStr(data[i].auth));
+ found = true;
+ }
+
+ if (jsonFormat)
+ {
+ longReply->cat("],\"err\":0}\n");
+ }
+ else if (!found)
+ {
+ longReply->cat(" none");
+ }
+ return GCodeResult::ok;
+ }
+
+ longReply->printf((jsonFormat) ? "{\"networkScanResults\":[],\"err\":1,\"errText\":\"%.s\"}" : "failed to retrieve scan results: %s", TranslateWiFiResponse(rslt));
}
- return GCodeResult::ok;
- }
+ break;
- longReply->printf((jsonFormat) ? "{\"rememberedNetworks\":[],\"err\":1,\"errText\":\"%.s\"}" : "Failed to retrieve network list: %s", TranslateWiFiResponse(rslt));
+ default:
+ break;
}
return GCodeResult::error;
@@ -1955,6 +2071,8 @@ void WiFiInterface::GetNewStatus() noexcept
case ResponseBufferTooSmall: return "response buffer too small";
case ResponseBadReplyFormatVersion: return "bad reply format version";
case ResponseBadParameter: return "bad parameter in request";
+ case ResponseNoScanStarted: return "no scan has been started";
+ case ResponseScanInProgress: return "scan still in progress";
case ResponseUnknownError: return "unknown error";
default: return "unknown response code";
}
diff --git a/src/Networking/ESP8266WiFi/WifiFirmwareUploader.cpp b/src/Networking/ESP8266WiFi/WifiFirmwareUploader.cpp
index 3160457c..ed4f46af 100644
--- a/src/Networking/ESP8266WiFi/WifiFirmwareUploader.cpp
+++ b/src/Networking/ESP8266WiFi/WifiFirmwareUploader.cpp
@@ -15,9 +15,7 @@
#include <Platform/RepRap.h>
#include <Storage/FileStore.h>
-#if WIFI_USES_ESP32
constexpr uint32_t Esp32FlashModuleSize = 4 * 1024 * 1024; // assume at least 4Mbytes flash
-#endif
// ESP8266 command codes
const uint8_t ESP_FLASH_BEGIN = 0x02; // Four 32-bit words: size to erase, number of data packets, data size in one packet, flash offset.
@@ -44,6 +42,16 @@ const size_t EspFlashBlockSize = 0x0400; // we send 1K byte blocks
const uint8_t ESP_IMAGE_MAGIC = 0xe9;
const uint8_t ESP_CHECKSUM_MAGIC = 0xef;
+// Status body lengths, used to initially identify ESP8266 v ESP32 devices
+const size_t ESP_BODY_LENGTHS[] = {0, 2, 4, 4};
+const char * const ESP_NAMES[] = {"unknown", "ESP8266", "ESP32", "ESP32+"};
+
+// Following address contains unique per device type id used to confirm ESP8266 and identify original ESP32
+const uint32_t CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000;
+
+// value stored at the above address (values taken from the esptool.py source)
+const uint32_t ESP_IDS[] = {0xffffffff, 0xfff0c101, 0x00f01d83, 0x0000000};
+
// Messages corresponding to result codes, should make sense when followed by " error"
const char * const resultMessages[] =
{
@@ -79,7 +87,7 @@ const char * const resultMessages[] =
static const uint32_t uploadBaudRates[] = { 230400, 115200, 74880, 9600 };
WifiFirmwareUploader::WifiFirmwareUploader(AsyncSerial& port, WiFiInterface& iface) noexcept
- : uploadPort(port), interface(iface), uploadFile(nullptr), state(UploadState::idle)
+ : uploadPort(port), interface(iface), uploadFile(nullptr), state(UploadState::idle), espType(ESPType::unknown)
{
}
@@ -407,7 +415,7 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::doCommand(uint8_t op
EspUploadResult stat = readPacket(op, valp, bodyLen, &status, msTimeout);
if (stat == EspUploadResult::success)
{
- if (!(bodyLen == 2 || bodyLen == 4))
+ if (espType != ESPType::unknown && bodyLen != ESP_BODY_LENGTHS[(unsigned int)espType])
{
stat = EspUploadResult::badReply;
}
@@ -436,23 +444,26 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::Sync(uint16_t timeou
buf[2] = 0x12;
buf[3] = 0x20;
- EspUploadResult stat = doCommand(ESP_SYNC, buf, sizeof(buf), 0, nullptr, timeout);
+ size_t bodyLen;
+ sendCommand(ESP_SYNC, 0, buf, sizeof(buf));
+ EspUploadResult stat = readPacket(ESP_SYNC, nullptr, bodyLen, nullptr, timeout);
// If we got a response other than sync, discard it and wait for a sync response. This happens at higher baud rates.
for (int i = 0; i < 10 && stat == EspUploadResult::respHeader; ++i)
{
- size_t bodyLen;
stat = readPacket(ESP_SYNC, nullptr, bodyLen, nullptr, timeout);
}
if (stat == EspUploadResult::success)
{
+ // Set the initial type of ESP based on status reply length
+ espType = (bodyLen == ESP_BODY_LENGTHS[ESPType::ESP32]) ? ESPType::ESP32 : ESPType::ESP8266;
+
// Read and discard additional replies
for (;;)
{
- size_t bodyLen;
EspUploadResult rc = readPacket(ESP_SYNC, nullptr, bodyLen, nullptr, defaultTimeout);
- if (rc != EspUploadResult::success || !(bodyLen == 2 || bodyLen == 4))
+ if (rc != EspUploadResult::success)
{
break;
}
@@ -470,46 +481,45 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashBegin(uint32_t
const uint32_t blkCnt = (size + (EspFlashBlockSize - 1)) / EspFlashBlockSize;
// Determine the erase size parameter to pass in the FLASH_BEGIN command
-#if WIFI_USES_ESP32
- const uint32_t erase_size = size;
-#else
- // Calculate the number of sectors to erase
- const uint32_t sector_size = 4 * 1024;
- uint32_t num_sectors = (size + (sector_size - 1))/sector_size;
+ uint32_t erase_size;
+ if (espType == ESPType::ESP8266)
+ {
+ // Calculate the number of sectors to erase
+ const uint32_t sector_size = 4 * 1024;
+ uint32_t num_sectors = (size + (sector_size - 1))/sector_size;
- const uint32_t start_sector = offset / sector_size;
- const uint32_t sectors_per_block = 16;
+ const uint32_t start_sector = offset / sector_size;
+ const uint32_t sectors_per_block = 16;
- uint32_t head_sectors = sectors_per_block - (start_sector % sectors_per_block);
- if (num_sectors < head_sectors)
- {
- head_sectors = num_sectors;
- }
+ uint32_t head_sectors = sectors_per_block - (start_sector % sectors_per_block);
+ if (num_sectors < head_sectors)
+ {
+ head_sectors = num_sectors;
+ }
- // SPI EraseArea function in the esp8266 ROM has a bug which causes extra area to be erased.
- // If the address range to be erased crosses the block boundary then extra head_sector_count sectors are erased.
- // If the address range doesn't cross the block boundary, then extra total_sector_count sectors are erased.
- if (num_sectors < 2 * head_sectors)
- {
- num_sectors = ((num_sectors + 1) / 2);
+ // SPI EraseArea function in the esp8266 ROM has a bug which causes extra area to be erased.
+ // If the address range to be erased crosses the block boundary then extra head_sector_count sectors are erased.
+ // If the address range doesn't cross the block boundary, then extra total_sector_count sectors are erased.
+ if (num_sectors < 2 * head_sectors)
+ {
+ num_sectors = ((num_sectors + 1) / 2);
+ }
+ else
+ {
+ num_sectors = (num_sectors - head_sectors);
+ }
+ erase_size = num_sectors * sector_size;
}
else
{
- num_sectors = (num_sectors - head_sectors);
+ erase_size = size;
}
- const uint32_t erase_size = num_sectors * sector_size;
-#endif
-
// begin the Flash process
-#if WIFI_USES_ESP32
const uint32_t buf[5] = { erase_size, blkCnt, EspFlashBlockSize, offset, 0 }; // last word means not encrypted
-#else
- const uint32_t buf[4] = { erase_size, blkCnt, EspFlashBlockSize, offset };
-#endif
const uint32_t timeout = (erase_size != 0) ? eraseTimeout : defaultTimeout;
- return doCommand(ESP_FLASH_BEGIN, (const uint8_t*)buf, sizeof(buf), 0, nullptr, timeout);
+ return doCommand(ESP_FLASH_BEGIN, (const uint8_t*)buf, (espType == ESPType::ESP32_PLUS) ? sizeof(buf) : sizeof(buf) - sizeof(uint32_t), 0, nullptr, timeout);
}
// Send a command to the device to terminate the Flash process
@@ -519,8 +529,6 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashFinish(bool reb
return doCommand(ESP_FLASH_END, (const uint8_t*)&data, sizeof(data), 0, nullptr, defaultTimeout);
}
-#if WIFI_USES_ESP32
-
WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashSpiSetParameters(uint32_t size) noexcept
{
const uint32_t buf[6] = { 0, size, 64 * 1024, 4 * 1024, 256, 0x0000FFFF }; // id, size, block size, sector size, page size, status mask
@@ -533,8 +541,6 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashSpiAttach() noe
return doCommand(ESP_SPI_ATTACH, (const uint8_t*)buf, sizeof(buf), 0, nullptr, defaultTimeout);
}
-#endif
-
// Compute the checksum of a block of data
uint16_t WifiFirmwareUploader::checksum(const uint8_t *data, uint16_t dataLen, uint16_t cksum) noexcept
{
@@ -555,7 +561,10 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashWriteBlock(uint
// Allocate a data buffer for the combined header and block data
const uint16_t dataOfst = 16;
const uint16_t blkBufSize = dataOfst + blkSize;
+#if !STM32
+ // On the STM32F4 our stack is not DMA capable, so we can't use the stack instead we allocate it when we open the file.
uint32_t blkBuf32[blkBufSize/4];
+#endif
uint8_t * const blkBuf = reinterpret_cast<uint8_t*>(blkBuf32);
// Prepare the header for the block
@@ -607,6 +616,36 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::DoErase(uint32_t add
return flashBegin(address, size);
}
+void WifiFirmwareUploader::Identify() noexcept
+{
+ if (espType != ESPType::unknown)
+ {
+ // sync will have done basics so we can safely use doCommand
+ const uint32_t addr = CHIP_DETECT_MAGIC_REG_ADDR;
+ uint32_t regVal;
+ EspUploadResult stat;
+
+ if ((stat = doCommand(ESP_READ_REG, (const uint8_t*)&addr, sizeof(addr), 0, &regVal, defaultTimeout)) == EspUploadResult::success)
+ {
+ //debugPrintf("read magic returns %x current type is %s\n", regVal, ESP_NAMES[espType]);
+ if (regVal == ESP_IDS[ESPType::ESP8266])
+ {
+ espType = ESPType::ESP8266;
+ }
+ else if (regVal == ESP_IDS[ESPType::ESP32])
+ {
+ espType = ESPType::ESP32;
+ }
+ else if (espType == ESPType::ESP32)
+ {
+ // we don't recognise it as ESP8266 or ESP32, but we think it is an ESP32 device. Assume a newer ESP32
+ espType = ESPType::ESP32_PLUS;
+ }
+ // if we don't have an explicit match use whatever sync decided
+ }
+ }
+}
+
void WifiFirmwareUploader::Spin() noexcept
{
switch (state)
@@ -628,8 +667,8 @@ void WifiFirmwareUploader::Spin() noexcept
// First attempt at this baud rate
MessageF("Trying to connect at %u baud: ", baud);
}
- uploadPort.begin(baud);
interface.ResetWiFiForUpload(false);
+ uploadPort.begin(baud);
lastAttemptTime = lastResetTime = millis();
state = UploadState::connecting;
}
@@ -644,28 +683,31 @@ void WifiFirmwareUploader::Spin() noexcept
if (res == EspUploadResult::success)
{
// Successful connection
-// MessageF(" success on attempt %d\n", (connectAttemptNumber % retriesPerBaudRate) + 1);
- MessageF(" success\n");
-#if WIFI_USES_ESP32
- res = flashSpiAttach();
- if (res != EspUploadResult::success)
+ Identify();
+ MessageF(" success, found %s\n", ESP_NAMES[espType]);
+ if (espType == ESPType::ESP8266)
{
- MessageF("Failed to attach SPI flash\n");
- state = UploadState::resetting; // try a reset and a lower baud rate
- break;
+ state = UploadState::erasing1;
}
- res = flashSpiSetParameters(Esp32FlashModuleSize);
- if (res != EspUploadResult::success)
+ else
{
- MessageF("Failed to set SPI parameters\n");
- state = UploadState::resetting; // try a reset and a lower baud rate
- break;
+ res = flashSpiAttach();
+ if (res != EspUploadResult::success)
+ {
+ MessageF("Failed to attach SPI flash\n");
+ state = UploadState::resetting; // try a reset and a lower baud rate
+ break;
+ }
+ res = flashSpiSetParameters(Esp32FlashModuleSize);
+ if (res != EspUploadResult::success)
+ {
+ MessageF("Failed to set SPI parameters\n");
+ state = UploadState::resetting; // try a reset and a lower baud rate
+ break;
+ }
+ MessageF("SPI flash parameters set\n");
+ state = UploadState::erasing2;
}
- MessageF("SPI flash parameters set\n");
- state = UploadState::erasing2;
-#else
- state = UploadState::erasing1;
-#endif
}
else
{
@@ -684,12 +726,9 @@ void WifiFirmwareUploader::Spin() noexcept
break;
case UploadState::erasing1:
-#if WIFI_USES_ESP32
- // no break
-#else
if (millis() - lastAttemptTime >= blockWriteInterval)
{
- uploadResult = DoErase(systemParametersAddress, systemParametersSize);
+ uploadResult = DoErase(esp8266systemParametersAddress, esp8266systemParametersSize);
if (uploadResult == EspUploadResult::success)
{
state = UploadState::erasing2;
@@ -701,7 +740,6 @@ void WifiFirmwareUploader::Spin() noexcept
}
}
break;
-#endif
case UploadState::erasing2:
if (millis() - lastAttemptTime >= blockWriteInterval)
@@ -755,6 +793,9 @@ void WifiFirmwareUploader::Spin() noexcept
case UploadState::done:
uploadFile->Close();
uploadPort.end(); // disable the port, it has a high interrupt priority
+#if STM32
+ DeleteObject(blkBuf32);
+#endif
if (uploadResult == EspUploadResult::success)
{
MessageF("Upload successful\n");
@@ -805,6 +846,22 @@ void WifiFirmwareUploader::SendUpdateFile(const char *file, uint32_t address) no
return;
}
+#if STM32
+ // we need a buffer that is DMA capable
+ const uint32_t blkSize = EspFlashBlockSize;
+
+ // Allocate a data buffer for the combined header and block data
+ const uint16_t dataOfst = 16;
+ const uint16_t blkBufSize = dataOfst + blkSize;
+ blkBuf32 = new uint32_t[blkBufSize/4];
+ if (blkBuf32 == nullptr)
+ {
+ uploadFile->Close();
+ MessageF("Unable to allocate upload buffer\n");
+ return;
+ }
+#endif
+
// Stop the network
restartModeOnCompletion = interface.EnableState();
interface.Stop();
diff --git a/src/Networking/ESP8266WiFi/WifiFirmwareUploader.h b/src/Networking/ESP8266WiFi/WifiFirmwareUploader.h
index 5ea07ed9..a589466b 100644
--- a/src/Networking/ESP8266WiFi/WifiFirmwareUploader.h
+++ b/src/Networking/ESP8266WiFi/WifiFirmwareUploader.h
@@ -37,10 +37,8 @@ private:
static const uint32_t eraseTimeout = 15000; // increased from 12 to 15 seconds because Roland's board was timing out
static const unsigned int percentToReportIncrement = 5; // how often we report % complete
-#if !WIFI_USES_ESP32
- static const uint32_t systemParametersAddress = 0x3FE000; // the address of the system + user parameter area that needs to be cleared when changing SDK version
- static const uint32_t systemParametersSize = 0x2000; // the size of the system + user parameter area
-#endif
+ static const uint32_t esp8266systemParametersAddress = 0x3FE000; // the address of the system + user parameter area that needs to be cleared when changing SDK version
+ static const uint32_t esp8266systemParametersSize = 0x2000; // the size of the system + user parameter area
// Return codes
// *** This list must be kept in step with the corresponding messages! ***
@@ -79,6 +77,16 @@ private:
done
};
+ // Type of ESP. We need to identify ESP8266, the original ESP32 and ESP3232S2 or later devices.
+ // These three classes of device need slightly different flash commands.
+ enum ESPType : uint8_t
+ {
+ unknown = 0,
+ ESP8266,
+ ESP32,
+ ESP32_PLUS
+ };
+
void MessageF(const char *fmt, ...) noexcept;
int ReadByte(uint8_t& data, bool slipDecode) noexcept;
void WriteByteRaw(uint8_t b) noexcept;
@@ -94,13 +102,12 @@ private:
EspUploadResult Sync(uint16_t timeout) noexcept;
EspUploadResult flashBegin(uint32_t offset, uint32_t size) noexcept;
EspUploadResult flashFinish(bool reboot) noexcept;
-#if WIFI_USES_ESP32
EspUploadResult flashSpiSetParameters(uint32_t size) noexcept;
EspUploadResult flashSpiAttach() noexcept;
-#endif
static uint16_t checksum(const uint8_t *data, uint16_t dataLen, uint16_t cksum) noexcept;
EspUploadResult flashWriteBlock(uint16_t flashParmVal, uint16_t flashParmMask) noexcept;
EspUploadResult DoErase(uint32_t address, uint32_t size) noexcept;
+ void Identify() noexcept;
AsyncSerial& uploadPort;
WiFiInterface& interface;
@@ -115,6 +122,10 @@ private:
UploadState state;
EspUploadResult uploadResult;
int restartModeOnCompletion;
+ ESPType espType;
+#if STM32
+ uint32_t *blkBuf32;
+#endif
};
#endif // HAS_WIFI_NETWORKING
diff --git a/src/Networking/Network.cpp b/src/Networking/Network.cpp
index 78cbd2d8..6105b0fb 100644
--- a/src/Networking/Network.cpp
+++ b/src/Networking/Network.cpp
@@ -104,21 +104,27 @@ Network::Network(Platform& p) noexcept : platform(p)
}
#if SUPPORT_OBJECT_MODEL
+
+// Macro to build a standard lambda function that includes the necessary type conversions
+#define OBJECT_MODEL_FUNC(_ret) OBJECT_MODEL_FUNC_BODY(Network, _ret)
+
// Object model table and functions
// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17.
// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM.
-constexpr ObjectModelArrayDescriptor Network::interfacesArrayDescriptor =
+constexpr ObjectModelArrayTableEntry Network::objectModelArrayTable[] =
{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((Network*)self)->GetNumNetworkInterfaces(); },
+ // 0. Interfaces
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((Network*)self)->GetNumNetworkInterfaces(); },
#if HAS_NETWORKING
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((Network*)self)->interfaces[context.GetIndex(0)]); }
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((Network*)self)->interfaces[context.GetLastIndex()]); }
#endif
+ }
};
-// Macro to build a standard lambda function that includes the necessary type conversions
-#define OBJECT_MODEL_FUNC(_ret) OBJECT_MODEL_FUNC_BODY(Network, _ret)
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(Network)
constexpr ObjectModelTableEntry Network::objectModelTable[] =
{
@@ -128,7 +134,7 @@ constexpr ObjectModelTableEntry Network::objectModelTable[] =
{ "corsSite", OBJECT_MODEL_FUNC(self->GetCorsSite()), ObjectModelEntryFlags::none },
# endif
{ "hostname", OBJECT_MODEL_FUNC(self->GetHostname()), ObjectModelEntryFlags::none },
- { "interfaces", OBJECT_MODEL_FUNC_NOSELF(&interfacesArrayDescriptor), ObjectModelEntryFlags::none },
+ { "interfaces", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
#endif
{ "name", OBJECT_MODEL_FUNC_NOSELF(reprap.GetName()), ObjectModelEntryFlags::none },
};
diff --git a/src/Networking/Network.h b/src/Networking/Network.h
index eef40cd5..8548f2f7 100644
--- a/src/Networking/Network.h
+++ b/src/Networking/Network.h
@@ -121,8 +121,7 @@ public:
uint32_t GetHttpReplySeq() noexcept;
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(interfaces)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
unsigned int GetNumNetworkInterfaces() const noexcept;
diff --git a/src/ObjectModel/GlobalVariables.cpp b/src/ObjectModel/GlobalVariables.cpp
index 0e532135..1e8a8fcf 100644
--- a/src/ObjectModel/GlobalVariables.cpp
+++ b/src/ObjectModel/GlobalVariables.cpp
@@ -14,37 +14,56 @@ const ObjectModelClassDescriptor *GlobalVariables::GetObjectModelClassDescriptor
// Construct a JSON representation of those parts of the object model requested by the user
// This overrides the standard definition because the variable names are not fixed
// We ignore any remaining key or flags and just report all the variables
-void GlobalVariables::ReportAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char *filter) const noexcept
- THROWS(GCodeException)
+void GlobalVariables::ReportAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char *filter) const THROWS(GCodeException)
{
- buf->cat('{');
- if (context.IncreaseDepth())
+ if (*filter == 0)
{
+ // Report all global variables
+ buf->cat('{');
+ if (context.IncreaseDepth())
{
- ReadLocker locker(lock); // make sure that no other task modifies the list while we are traversing it
- vars.IterateWhile([this, buf, &context, classDescriptor, filter](unsigned int index, const Variable& v) noexcept -> bool
- {
- buf->catf((index != 0) ? ",\"%s\":" : "\"%s\":", v.GetName().Ptr());
- ReportItemAsJsonFull(buf, context, classDescriptor, v.GetValue(), filter);
- return true;
- }
- );
+ {
+ ReadLocker locker(lock); // make sure that no other task modifies the list while we are traversing it
+ vars.IterateWhile([this, buf, &context, classDescriptor, filter](unsigned int index, const Variable& v) noexcept -> bool
+ {
+ buf->catf((index != 0) ? ",\"%s\":" : "\"%s\":", v.GetName().Ptr());
+ ReportItemAsJson(buf, context, classDescriptor, v.GetValue(), filter);
+ return true;
+ }
+ );
+ }
+ context.DecreaseDepth();
+ }
+ buf->cat('}');
+ }
+ else
+ {
+ // Report a specific global variable, or part of one
+ const char *pos = GetNextElement(filter); // find the end of the variable name
+ const Variable *const var = vars.Lookup(filter, pos - filter);
+ if (var == nullptr)
+ {
+ buf->cat("null");
+ }
+ else if (context.IncreaseDepth())
+ {
+ ReportItemAsJson(buf, context, nullptr, var->GetValue(), pos);
+ }
+ else
+ {
+ buf->cat("{}");
}
- context.DecreaseDepth();
}
- buf->cat('}');
}
ReadLockedPointer<const VariableSet> GlobalVariables::GetForReading() noexcept
{
- ReadLocker locker(lock);
- return ReadLockedPointer<const VariableSet>(locker, &vars);
+ return ReadLockedPointer<const VariableSet>(lock, &vars);
}
WriteLockedPointer<VariableSet> GlobalVariables::GetForWriting() noexcept
{
- WriteLocker locker(lock);
- return WriteLockedPointer<VariableSet>(locker, &vars);
+ return WriteLockedPointer<VariableSet>(lock, &vars);
}
// End
diff --git a/src/ObjectModel/GlobalVariables.h b/src/ObjectModel/GlobalVariables.h
index b58df922..97e007a3 100644
--- a/src/ObjectModel/GlobalVariables.h
+++ b/src/ObjectModel/GlobalVariables.h
@@ -27,7 +27,7 @@ protected:
// Construct a JSON representation of those parts of the object model requested by the user
// This overrides the standard definition because the variable names are not fixed
- void ReportAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char *_ecv_array filter) const noexcept override
+ void ReportAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char *_ecv_array filter) const override
THROWS(GCodeException);
private:
diff --git a/src/ObjectModel/ObjectModel.cpp b/src/ObjectModel/ObjectModel.cpp
index 8fa383fd..b7fbf366 100644
--- a/src/ObjectModel/ObjectModel.cpp
+++ b/src/ObjectModel/ObjectModel.cpp
@@ -83,6 +83,15 @@ void ExpressionValue::AppendAsString(const StringRef& str) const noexcept
}
break;
+ case TypeCode::Duration:
+ {
+ unsigned int hours = uVal/3600,
+ minutes = (uVal / 60) % 60,
+ seconds = uVal % 60;
+ str.catf("%u:%02u:%02u", hours, minutes, seconds);
+ }
+ break;
+
case TypeCode::DriverId_tc:
#if SUPPORT_CAN_EXPANSION
str.catf("%u.%u", (unsigned int)param, (unsigned int)uVal);
@@ -114,33 +123,136 @@ void ExpressionValue::AppendAsString(const StringRef& str) const noexcept
#endif
break;
+ case TypeCode::ObjectModelArray:
+ str.cat('{');
+ {
+ const ObjectModelArrayTableEntry *entry = omVal->FindObjectModelArrayEntry(param & 0xFF);
+ if (entry == nullptr)
+ {
+ str.cat("error");
+ }
+ else
+ {
+ ObjectExplorationContext context;
+ context.AddIndex(param >> 8); // in case it is a 2D array
+ ReadLocker lock(entry->lockPointer);
+ const size_t count = entry->GetNumElements(omVal, context);
+ for (size_t i = 0; i < count; ++i)
+ {
+ if (i != 0)
+ {
+ str.cat(',');
+ }
+ context.AddIndex(i);
+ entry->GetElement(omVal, context).AppendAsString(str);
+ context.RemoveIndex();
+ }
+ }
+ }
+ str.cat('}');
+ break;
+
+ case TypeCode::HeapArray:
+ str.cat('{');
+ {
+ ReadLocker lock(Heap::heapLock);
+ const size_t count = ahVal.GetNumElements();
+ for (size_t i = 0; i < count; ++i)
+ {
+ if (i != 0)
+ {
+ str.cat(',');
+ }
+ ExpressionValue val;
+ ahVal.GetElement(i, val);
+ val.AppendAsString(str);
+ }
+ }
+ str.cat('}');
+ break;
+
+ case TypeCode::Port:
+ iopVal->AppendPinName(str);
+ break;
+
+ case TypeCode::UniqueId_tc:
+ uniqueIdVal->AppendCharsToString(str);
+ break;
+
// We don't fully handle the remaining types
case TypeCode::ObjectModel_tc:
str.cat("{object}");
break;
- case TypeCode::Array:
- str.cat("[array]");
- break;
-
case TypeCode::Bitmap16:
case TypeCode::Bitmap32:
case TypeCode::Bitmap64:
- str.cat("(Bitmap)");
+ str.cat("{Bitmap}");
break;
case TypeCode::Enum32:
- str.cat("(enumeration)");
- break;
-
- case TypeCode::Port:
- iopVal->AppendPinName(str);
+ str.cat("{enumeration}");
break;
+ }
+}
- case TypeCode::UniqueId_tc:
- uniqueIdVal->AppendCharsToString(str);
- break;
+// Compare two values that are assumed to be represented in the same way
+bool ExpressionValue::operator==(const ExpressionValue& other) const noexcept
+{
+ if (type == other.type)
+ {
+ switch (GetType())
+ {
+ case TypeCode::None:
+ return true;
+
+ case TypeCode::Bool:
+ return bVal == other.bVal;
+
+ case TypeCode::Char:
+ return cVal == other.cVal;
+
+ case TypeCode::CString:
+ return strcmp(sVal, other.sVal) == 0;
+
+ case TypeCode::HeapString:
+ return strcmp(shVal.Get().Ptr(), other.shVal.Get().Ptr()) == 0;
+
+ case TypeCode::Float:
+ return fVal == other.fVal;
+
+ case TypeCode::Uint32:
+ case TypeCode::IPAddress_tc:
+ case TypeCode::Int32:
+ case TypeCode::Bitmap16:
+ case TypeCode::Bitmap32:
+ case TypeCode::Enum32:
+ case TypeCode::Duration:
+ return uVal == other.uVal;
+
+ case TypeCode::Uint64:
+ case TypeCode::Bitmap64:
+ case TypeCode::DateTime_tc:
+ case TypeCode::MacAddress_tc:
+ case TypeCode::DriverId_tc:
+ return uVal == other.uVal && param == other.param;
+
+ case TypeCode::Port:
+ return iopVal == other.iopVal;
+
+ // We don't handle the remaining types
+ case TypeCode::Special:
+ case TypeCode::ObjectModel_tc:
+ case TypeCode::ObjectModelArray:
+ case TypeCode::HeapArray:
+ case TypeCode::UniqueId_tc:
+#if SUPPORT_CAN_EXPANSION
+ case TypeCode::CanExpansionBoardDetails:
+#endif
+ return false;
+ }
}
+ return false;
}
ExpressionValue::ExpressionValue(const ExpressionValue& other) noexcept
@@ -221,6 +333,24 @@ void ExpressionValue::SetDriverId(DriverId did) noexcept
uVal = did.localDriver;
}
+bool ExpressionValue::IsHeapStringArrayType() const noexcept
+{
+ if (type != (uint32_t)TypeCode::HeapArray)
+ {
+ return false;
+ }
+
+ const size_t numElems = ahVal.GetNumElements();
+ for (size_t i = 0; i < numElems; ++i)
+ {
+ if (ahVal.GetElementType(i) != TypeCode::HeapString)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
#if SUPPORT_CAN_EXPANSION
// Given that this is a CanExpansionBoardDetails value, extract the part requested according to the parameter and append it to the string
@@ -391,22 +521,30 @@ ObjectExplorationContext::ObjectExplorationContext(const GCodeBuffer *_ecv_null
{
}
-int32_t ObjectExplorationContext::GetIndex(size_t n) const THROWS(GCodeException)
+// Constructor used for generating a context to pass array indices
+ObjectExplorationContext::ObjectExplorationContext() noexcept
+ : ObjectExplorationContext(nullptr, false, false, -1, -1) { }
+
+int32_t ObjectExplorationContext::GetIndex(size_t n) const noexcept
{
if (n < numIndicesCounted)
{
return indices[numIndicesCounted - n - 1];
}
- THROW_INTERNAL_ERROR;
+ // We can't throw from this function because it is called by lambdas in the object model tables and we don't want exception tables for all of those
+ REPORT_INTERNAL_ERROR;
+ return 0;
}
-int32_t ObjectExplorationContext::GetLastIndex() const THROWS(GCodeException)
+int32_t ObjectExplorationContext::GetLastIndex() const noexcept
{
if (numIndicesCounted != 0)
{
return indices[numIndicesCounted - 1];
}
- THROW_INTERNAL_ERROR;
+ // We can't throw from this function because it is called by lambdas in the object model tables and we don't want exception tables for all of those
+ REPORT_INTERNAL_ERROR;
+ return 0;
}
bool ObjectExplorationContext::ShouldReport(const ObjectModelEntryFlags f) const noexcept
@@ -461,6 +599,7 @@ void ObjectModel::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& cont
classDescriptor = GetObjectModelClassDescriptor();
}
+ // Loop doing this object followed by its ancestors
while (classDescriptor != nullptr)
{
const uint8_t * const descriptor = classDescriptor->omd;
@@ -477,6 +616,7 @@ void ObjectModel::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& cont
{
if (tbl->Matches(filter, context))
{
+ ReadLocker lock(GetObjectLock(tableNumber));
if (tbl->ReportAsJson(buf, context, classDescriptor, this, filter, !added))
{
added = true;
@@ -524,46 +664,19 @@ void ObjectModel::ReportAsJson(const GCodeBuffer *_ecv_null gb, OutputBuffer *bu
}
}
-// Function to report a value or object as JSON
-// This function is recursive, so keep its stack usage low.
-// Most recursive calls are for non-array object values, so handle object values inline to reduce stack usage.
-// This saves about 240 bytes of stack space but costs 272 bytes of flash memory.
-inline void ObjectModel::ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor,
- const ExpressionValue& val, const char *_ecv_array filter) const THROWS(GCodeException)
+void ObjectModel::ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ExpressionValue& val) const noexcept
{
- if (context.WantArrayLength() && *filter == 0)
- {
- ReportArrayLengthAsJson(buf, context, val);
- }
- else if (val.GetType() == TypeCode::ObjectModel_tc)
+ switch (val.GetType())
{
- if ( (*filter != '.' && *filter != 0) // we should have reached the end of the filter or a '.', error if not
- || val.omVal == nullptr // OM arrays may contain null entries, so we need to handle them here
- )
+ case TypeCode::ObjectModelArray:
{
- buf->cat("null");
+ const ObjectModelArrayTableEntry *const entry = val.omVal->GetObjectModelArrayEntry(val.param & 0xFF);
+ buf->catf("%u", entry->GetNumElements(this, context));
}
- else
- {
- if (*filter == '.')
- {
- ++filter;
- }
- val.omVal->ReportAsJson(buf, context, (val.omVal == this) ? classDescriptor : nullptr, val.param, filter);
- }
- }
- else
- {
- ReportItemAsJsonFull(buf, context, classDescriptor, val, filter);
- }
-}
+ break;
-void ObjectModel::ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ExpressionValue& val) const noexcept
-{
- switch (val.GetType())
- {
- case TypeCode::Array:
- buf->catf("%u", val.omadVal->GetNumElements(this, context));
+ case TypeCode::HeapArray:
+ buf->catf("%u", val.ahVal.GetNumElements());
break;
case TypeCode::Bitmap16:
@@ -590,25 +703,74 @@ void ObjectModel::ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationCo
}
// Function to report a value or object as JSON
-// This function is recursive, so keep its stack usage low
+// This function is recursive, so keep its stack usage low.
+// The type of 'val' may not be ObjectModel_tc. That type is handled in function ReportItemAsJson, which is declared 'inline' to reduce stack usage.
void ObjectModel::ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor,
const ExpressionValue& val, const char *filter) const THROWS(GCodeException)
{
switch (val.GetType())
{
- case TypeCode::Array:
+ case TypeCode::ObjectModelArray:
+ {
+ const ObjectModelArrayTableEntry *const entry = val.omVal->GetObjectModelArrayEntry(val.param & 0xFF);
+ if (*filter == '[')
+ {
+ ++filter;
+ if (*filter == ']') // if reporting on [parts of] all elements in the array
+ {
+ ReportObjectModelArrayAsJson(buf, context, classDescriptor, entry, filter + 1);
+ }
+ else
+ {
+ const char *endptr;
+ const int32_t index = StrToI32(filter, &endptr);
+ if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= entry->GetNumElements(this, context))
+ {
+ buf->cat("null"); // avoid returning badly-formed JSON
+ break; // invalid syntax, or index out of range
+ }
+ if (*filter == 0)
+ {
+ buf->cat('[');
+ }
+ context.AddIndex(index);
+ {
+ // As at release 3.1.1 this next block uses the most stack of this entire function
+ ReadLocker lock(entry->lockPointer);
+ const ExpressionValue element = entry->GetElement(this, context);
+ ReportItemAsJson(buf, context, classDescriptor, element, endptr + 1);
+ }
+ context.RemoveIndex();
+ if (*filter == 0)
+ {
+ buf->cat(']');
+ }
+ }
+ }
+ else if (*filter == 0) // else reporting on all subparts of all elements in the array, or just the length
+ {
+ ReportObjectModelArrayAsJson(buf, context, classDescriptor, entry, filter);
+ }
+ else
+ {
+ buf->cat("null");
+ }
+ }
+ break;
+
+ case TypeCode::HeapArray:
if (*filter == '[')
{
++filter;
if (*filter == ']') // if reporting on [parts of] all elements in the array
{
- ReportArrayAsJson(buf, context, classDescriptor, val.omadVal, filter + 1);
+ ReportHeapArrayAsJson(buf, context, classDescriptor, val.ahVal, filter + 1);
}
else
{
const char *endptr;
const int32_t index = StrToI32(filter, &endptr);
- if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context))
+ if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.ahVal.GetNumElements())
{
buf->cat("null"); // avoid returning badly-formed JSON
break; // invalid syntax, or index out of range
@@ -617,14 +779,11 @@ void ObjectModel::ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationConte
{
buf->cat('[');
}
- context.AddIndex(index);
- {
- // As at release 3.1.1 this next block uses the most stack of this entire function
- ReadLocker lock(val.omadVal->lockPointer);
- const ExpressionValue element = val.omadVal->GetElement(this, context);
- ReportItemAsJson(buf, context, classDescriptor, element, endptr + 1);
- }
- context.RemoveIndex();
+
+ ExpressionValue element;
+ val.ahVal.GetElement((size_t)index, element);
+ ReportItemAsJson(buf, context, classDescriptor, element, endptr + 1);
+
if (*filter == 0)
{
buf->cat(']');
@@ -633,7 +792,7 @@ void ObjectModel::ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationConte
}
else if (*filter == 0) // else reporting on all subparts of all elements in the array, or just the length
{
- ReportArrayAsJson(buf, context, classDescriptor, val.omadVal, filter);
+ ReportHeapArrayAsJson(buf, context, classDescriptor, val.ahVal, filter);
}
else
{
@@ -641,36 +800,6 @@ void ObjectModel::ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationConte
}
break;
- case TypeCode::Float:
- ReportFloat(buf, val);
- break;
-
- case TypeCode::Uint32:
- buf->catf("%" PRIu32, val.uVal);
- break;
-
- case TypeCode::Uint64:
- buf->catf("%" PRIu64, ((uint64_t)val.param << 32) | val.uVal); // convert unsigned integer to string
- break;
-
- case TypeCode::Int32:
- buf->catf("%" PRIi32, val.iVal);
- break;
-
- case TypeCode::CString:
- buf->catf("\"%.s\"", val.sVal);
- break;
-
- case TypeCode::HeapString:
- buf->catf("\"%.s\"", val.shVal.Get().Ptr());
- break;
-
-#if SUPPORT_CAN_EXPANSION
- case TypeCode::CanExpansionBoardDetails:
- ReportExpansionBoardDetail(buf, val);
- break;
-#endif
-
case TypeCode::Bitmap16:
case TypeCode::Bitmap32:
if (*filter == '[')
@@ -738,86 +867,138 @@ void ObjectModel::ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationConte
ReportBitmap64Long(buf, val);
break;
- case TypeCode::Enum32:
- if (context.ShortFormReport())
+ default:
+ // Only primitive types remain so we should have reached the end of the filter string
+ if (*filter != 0)
{
- buf->catf("%" PRIu32, val.uVal);
+ buf->cat("null");
}
else
{
- buf->cat("\"unimplemented\"");
- // TODO append the real name
- }
- break;
+ switch (val.GetType())
+ {
+ case TypeCode::Float:
+ ReportFloat(buf, val);
+ break;
- case TypeCode::Bool:
- buf->cat((val.bVal) ? "true" : "false");
- break;
+ case TypeCode::Uint32:
+ buf->catf("%" PRIu32, val.uVal);
+ break;
- case TypeCode::Char:
- buf->cat('"');
- buf->EncodeChar(val.cVal);
- buf->cat('"');
- break;
+ case TypeCode::Uint64:
+ buf->catf("%" PRIu64, ((uint64_t)val.param << 32) | val.uVal); // convert unsigned integer to string
+ break;
- case TypeCode::IPAddress_tc:
- {
- const IPAddress ipVal(val.uVal);
- char sep = '"';
- for (unsigned int q = 0; q < 4; ++q)
- {
- buf->catf("%c%u", sep, ipVal.GetQuad(q));
- sep = '.';
- }
- buf->cat('"');
- }
- break;
+ case TypeCode::Int32:
+ buf->catf("%" PRIi32, val.iVal);
+ break;
- case TypeCode::DateTime_tc:
- ReportDateTime(buf, val);
- break;
+ case TypeCode::CString:
+ buf->catf("\"%.s\"", val.sVal);
+ break;
- case TypeCode::DriverId_tc:
+ case TypeCode::HeapString:
+ buf->catf("\"%.s\"", val.shVal.Get().Ptr());
+ break;
+
+#if SUPPORT_CAN_EXPANSION
+ case TypeCode::CanExpansionBoardDetails:
+ ReportExpansionBoardDetail(buf, val);
+ break;
+#endif
+
+ case TypeCode::Enum32:
+ if (context.ShortFormReport())
+ {
+ buf->catf("%" PRIu32, val.uVal);
+ }
+ else
+ {
+ buf->cat("\"unimplemented\"");
+ // TODO append the real name
+ }
+ break;
+
+ case TypeCode::Bool:
+ buf->cat((val.bVal) ? "true" : "false");
+ break;
+
+ case TypeCode::Char:
+ buf->cat('"');
+ buf->EncodeChar(val.cVal);
+ buf->cat('"');
+ break;
+
+ case TypeCode::IPAddress_tc:
+ {
+ const IPAddress ipVal(val.uVal);
+ char sep = '"';
+ for (unsigned int q = 0; q < 4; ++q)
+ {
+ buf->catf("%c%u", sep, ipVal.GetQuad(q));
+ sep = '.';
+ }
+ buf->cat('"');
+ }
+ break;
+
+ case TypeCode::DateTime_tc:
+ ReportDateTime(buf, val);
+ break;
+
+ case TypeCode::Duration:
+ {
+ unsigned int hours = val.uVal/3600,
+ minutes = (val.uVal / 60) % 60,
+ seconds = val.uVal % 60;
+ buf->catf("%u:%02u:%02u", hours, minutes, seconds);
+ }
+ break;
+
+ case TypeCode::DriverId_tc:
#if SUPPORT_CAN_EXPANSION
- buf->catf("\"%u.%u\"", (unsigned int)val.param, (unsigned int)val.uVal);
+ buf->catf("\"%u.%u\"", (unsigned int)val.param, (unsigned int)val.uVal);
#else
- buf->catf("\"%u\"", (unsigned int)val.uVal);
+ buf->catf("\"%u\"", (unsigned int)val.uVal);
#endif
- break;
+ break;
- case TypeCode::MacAddress_tc:
- buf->catf("\"%02x:%02x:%02x:%02x:%02x:%02x\"",
- (unsigned int)(val.uVal & 0xFF), (unsigned int)((val.uVal >> 8) & 0xFF), (unsigned int)((val.uVal >> 16) & 0xFF), (unsigned int)((val.uVal >> 24) & 0xFF),
- (unsigned int)(val.param & 0xFF), (unsigned int)((val.param >> 8) & 0xFF));
- break;
+ case TypeCode::MacAddress_tc:
+ buf->catf("\"%02x:%02x:%02x:%02x:%02x:%02x\"",
+ (unsigned int)(val.uVal & 0xFF), (unsigned int)((val.uVal >> 8) & 0xFF), (unsigned int)((val.uVal >> 16) & 0xFF), (unsigned int)((val.uVal >> 24) & 0xFF),
+ (unsigned int)(val.param & 0xFF), (unsigned int)((val.param >> 8) & 0xFF));
+ break;
- case TypeCode::Special:
+ case TypeCode::Special:
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
- switch ((ExpressionValue::SpecialType)val.param)
- {
- case ExpressionValue::SpecialType::sysDir:
- buf->catf("\"%.s\"", reprap.GetPlatform().GetSysDir().Ptr());
- break;
- }
+ switch ((ExpressionValue::SpecialType)val.param)
+ {
+ case ExpressionValue::SpecialType::sysDir:
+ buf->catf("\"%.s\"", reprap.GetPlatform().GetSysDir().Ptr());
+ break;
+ }
#endif
- break;
+ break;
- case TypeCode::None:
- buf->cat("null");
- break;
+ case TypeCode::None:
+ buf->cat("null");
+ break;
- case TypeCode::Port:
- ReportPinNameAsJson(buf, val);
- break;
+ case TypeCode::Port:
+ ReportPinNameAsJson(buf, val);
+ break;
- case TypeCode::UniqueId_tc:
- buf->cat('"');
- val.uniqueIdVal->AppendCharsToBuffer(buf);
- buf->cat('"');
- break;
+ case TypeCode::UniqueId_tc:
+ buf->cat('"');
+ val.uniqueIdVal->AppendCharsToBuffer(buf);
+ buf->cat('"');
+ break;
- case TypeCode::ObjectModel_tc:
- break; // we already handled this case in the inline part
+ case TypeCode::ObjectModel_tc: // we already handled this case in the inline part
+ default: // to keep gcc happy
+ break;
+ }
+ }
}
}
@@ -832,14 +1013,14 @@ void ObjectModel::ReportPinNameAsJson(OutputBuffer *buf, const ExpressionValue&
}
// Report an entire array as JSON
-void ObjectModel::ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor,
- const ObjectModelArrayDescriptor *omad, const char *_ecv_array filter) const THROWS(GCodeException)
+void ObjectModel::ReportObjectModelArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor,
+ const ObjectModelArrayTableEntry *entry, const char *_ecv_array filter) const THROWS(GCodeException)
{
const bool isRootArray = (buf->Length() == context.GetInitialBufferOffset()); // it's a root array if we haven't started writing to the buffer yet
- ReadLocker lock(omad->lockPointer);
+ ReadLocker lock(entry->lockPointer);
buf->cat('[');
- const size_t count = omad->GetNumElements(this, context);
+ const size_t count = entry->GetNumElements(this, context);
const size_t startElement = (isRootArray) ? context.GetStartElement() : 0;
for (size_t i = startElement; i < count; ++i)
{
@@ -855,7 +1036,7 @@ void ObjectModel::ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext&
buf->cat(',');
}
context.AddIndex(i);
- const ExpressionValue element = omad->GetElement(this, context);
+ const ExpressionValue element = entry->GetElement(this, context);
ReportItemAsJson(buf, context, classDescriptor, element, filter);
context.RemoveIndex();
}
@@ -866,6 +1047,40 @@ void ObjectModel::ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext&
buf->cat(']');
}
+// Report an entire array as JSON
+void ObjectModel::ReportHeapArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor,
+ ArrayHandle ah, const char *_ecv_array filter) const THROWS(GCodeException)
+{
+ const bool isRootArray = (buf->Length() == context.GetInitialBufferOffset()); // it's a root array if we haven't started writing to the buffer yet
+ buf->cat('[');
+
+ ReadLocker lock(Heap::heapLock);
+ const size_t count = ah.GetNumElements();
+ const size_t startElement = (isRootArray) ? context.GetStartElement() : 0;
+ for (size_t i = startElement; i < count; ++i)
+ {
+ // Support retrieving just part of the array in case it is too large to write all of it to the buffer
+ if (i != startElement)
+ {
+ if (isRootArray && buf->Length() >= (OUTPUT_BUFFER_SIZE * (OUTPUT_BUFFER_COUNT - RESERVED_OUTPUT_BUFFERS))/2)
+ {
+ // We've used half the buffer space already, so stop reporting
+ context.SetNextElement(i);
+ break;
+ }
+ buf->cat(',');
+ }
+ ExpressionValue element;
+ ah.GetElement(i, element);
+ ReportItemAsJson(buf, context, classDescriptor, element, filter);
+ }
+ if (isRootArray && context.GetNextElement() < 0)
+ {
+ context.SetNextElement(0);
+ }
+ buf->cat(']');
+}
+
// Find the requested entry
const ObjectModelTableEntry* ObjectModel::FindObjectModelTableEntry(const ObjectModelClassDescriptor *classDescriptor, uint8_t tableNumber, const char *_ecv_array idString) const noexcept
{
@@ -922,7 +1137,7 @@ bool ObjectModelTableEntry::Matches(const char* filterString, const ObjectExplor
}
// Add the value of this element to the buffer, returning true if it matched and we did
-bool ObjectModelTableEntry::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModel *self, const char* filter, bool first) const noexcept
+bool ObjectModelTableEntry::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModel *self, const char* filter, bool first) const THROWS(GCodeException)
{
const char * nextElement = ObjectModel::GetNextElement(filter);
const ExpressionValue val = func(self, context);
@@ -970,6 +1185,7 @@ decrease(strlen(idString)) // recursion variant
classDescriptor = GetObjectModelClassDescriptor();
}
+ // Loop through this class and its ancestors
while (classDescriptor != nullptr)
{
const ObjectModelTableEntry * const e = FindObjectModelTableEntry(classDescriptor, tableNumber, idString);
@@ -982,6 +1198,7 @@ decrease(strlen(idString)) // recursion variant
idString = GetNextElement(idString);
const ExpressionValue val = e->func(this, context);
context.CheckStack(StackUsage::GetObjectValue_noTable);
+ ReadLocker lock(GetObjectLock(tableNumber));
return GetObjectValue(context, classDescriptor, val, idString);
}
if (tableNumber != 0)
@@ -1009,16 +1226,50 @@ decrease(strlen(idString)) // recursion variant
switch (val.GetType())
{
- case TypeCode::Array:
+ case TypeCode::ObjectModelArray:
+ {
+ if (*idString == 0)
+ {
+ if (context.WantArrayLength())
+ {
+ const ObjectModelArrayTableEntry *const entry = val.omVal->GetObjectModelArrayEntry(val.param & 0xFF);
+ ReadLocker lock(entry->lockPointer);
+ return ExpressionValue((int32_t)entry->GetNumElements(this, context));
+ }
+ return val;
+ }
+ if (*idString != '^')
+ {
+ throw context.ConstructParseException("missing array index");
+ }
+
+ context.AddIndex();
+ const ObjectModelArrayTableEntry *const entry = val.omVal->GetObjectModelArrayEntry(val.param & 0xFF);
+ ReadLocker lock(entry->lockPointer);
+
+ if (context.GetLastIndex() < 0 || (size_t)context.GetLastIndex() >= entry->GetNumElements(this, context))
+ {
+ if (context.WantExists())
+ {
+ return ExpressionValue(false);
+ }
+ throw context.ConstructParseException("array index out of bounds");
+ }
+
+ const ExpressionValue arrayElement = entry->GetElement(this, context);
+ context.CheckStack(StackUsage::GetObjectValue_noTable);
+ return GetObjectValue(context, classDescriptor, arrayElement, idString + 1);
+ }
+
+ case TypeCode::HeapArray:
{
if (*idString == 0)
{
if (context.WantArrayLength())
{
- ReadLocker lock(val.omadVal->lockPointer);
- return ExpressionValue((int32_t)val.omadVal->GetNumElements(this, context));
+ return ExpressionValue((int32_t)val.ahVal.GetNumElements());
}
- return ExpressionValue(static_cast<const ObjectModelArrayDescriptor*>(nullptr)); // return a dummy array so that caller can report "[array]" or compare it with null
+ return val;
}
if (*idString != '^')
{
@@ -1026,9 +1277,9 @@ decrease(strlen(idString)) // recursion variant
}
context.AddIndex();
- ReadLocker lock(val.omadVal->lockPointer);
+ ReadLocker lock(Heap::heapLock);
- if (context.GetLastIndex() < 0 || (size_t)context.GetLastIndex() >= val.omadVal->GetNumElements(this, context))
+ if (context.GetLastIndex() < 0 || (size_t)context.GetLastIndex() >= val.ahVal.GetNumElements())
{
if (context.WantExists())
{
@@ -1037,7 +1288,8 @@ decrease(strlen(idString)) // recursion variant
throw context.ConstructParseException("array index out of bounds");
}
- const ExpressionValue arrayElement = val.omadVal->GetElement(this, context);
+ ExpressionValue arrayElement;
+ val.ahVal.GetElement((size_t)context.GetLastIndex(), arrayElement);
context.CheckStack(StackUsage::GetObjectValue_noTable);
return GetObjectValue(context, classDescriptor, arrayElement, idString + 1);
}
diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h
index 3d05ac8c..05228fcc 100644
--- a/src/ObjectModel/ObjectModel.h
+++ b/src/ObjectModel/ObjectModel.h
@@ -10,45 +10,17 @@
#include <RepRapFirmware.h>
#include <GCodes/GCodeException.h>
-#include <Platform/Heap.h>
+#include <Platform/StringHandle.h>
+#include <Platform/ArrayHandle.h>
#if SUPPORT_OBJECT_MODEL
+#include "TypeCode.h"
#include <General/IPAddress.h>
#include <General/Bitmap.h>
#include <RTOSIface/RTOSIface.h>
#include <Networking/NetworkDefs.h>
-
-// Type codes to indicate what type of expression we have and how it is represented.
-// The "Special" type is for items that we have to evaluate when we are ready to write them out, in particular strings whose storage might disappear.
-enum class TypeCode : uint8_t
-{
- None = 0,
- Bool,
- Char,
- Uint32,
- Int32,
- Uint64, // only 56 bits actually available
- Float,
- Bitmap16,
- Bitmap32,
- Bitmap64, // only 56 bits actually available
- Enum32,
- ObjectModel_tc, // renamed for eCv to avoid clash with class ObjectModel
- CString,
- HeapString,
- IPAddress_tc, // renamed for eCv to avoid clash with class IPAddress in RRFLibraries
- Array,
- DateTime_tc, // renamed for eCv to avoid clash with class DateTime
- DriverId_tc, // renamed for eCv to avoid clash with class DriverId
- MacAddress_tc, // renamed for eCv to avoid clash with class MacAddress
- Special,
- Port,
- UniqueId_tc,
-#if SUPPORT_CAN_EXPANSION
- CanExpansionBoardDetails
-#endif
-};
+#include <Platform/OutputMemory.h>
#if SUPPORT_CAN_EXPANSION
@@ -63,7 +35,7 @@ enum class ExpansionDetail : uint32_t
// Forward declarations
class ObjectModel;
-class ObjectModelArrayDescriptor;
+class ObjectModelArrayTableEntry;
class ObjectModelTableEntry;
class IoPort;
class UniqueId;
@@ -91,14 +63,15 @@ struct ExpressionValue
uint32_t uVal; // used for enumerations, bitmaps and IP addresses (not for integers, we always use int32_t for those)
const char *_ecv_array sVal;
const ObjectModel *omVal; // object of some class derived from ObjectModel
- const ObjectModelArrayDescriptor *omadVal;
StringHandle shVal;
+ ArrayHandle ahVal;
const IoPort *iopVal;
const UniqueId *uniqueIdVal;
uint32_t whole; // a member we can use to copy the whole thing safely, at least as big as all the others. Assumes all other members are trivially copyable.
};
static_assert(sizeof(whole) >= sizeof(shVal));
+ static_assert(sizeof(whole) >= sizeof(ahVal));
static_assert(sizeof(whole) >= sizeof(omVal));
static_assert(sizeof(whole) >= sizeof(fVal));
@@ -117,8 +90,8 @@ struct ExpressionValue
explicit ExpressionValue(uint64_t u) noexcept : type((uint32_t)TypeCode::Uint64) { Set56BitValue(u); }
explicit constexpr ExpressionValue(const ObjectModel* null om) noexcept : type((om == nullptr) ? (uint32_t)TypeCode::None : (uint32_t)TypeCode::ObjectModel_tc), param(0), omVal(om) { }
constexpr ExpressionValue(const ObjectModel *null om, uint8_t tableNumber) noexcept : type((om == nullptr) ? (uint32_t)TypeCode::None : (uint32_t)TypeCode::ObjectModel_tc), param(tableNumber), omVal(om) { }
+ constexpr ExpressionValue(const ObjectModel *om, uint8_t arrayNumber, bool dummy) noexcept : type((uint32_t)TypeCode::ObjectModelArray), param(arrayNumber), omVal(om) { }
explicit constexpr ExpressionValue(const char *_ecv_array s) noexcept : type((uint32_t)TypeCode::CString), param(0), sVal(s) { }
- explicit constexpr ExpressionValue(const ObjectModelArrayDescriptor *omad) noexcept : type((uint32_t)TypeCode::Array), param(0), omadVal(omad) { }
explicit constexpr ExpressionValue(IPAddress ip) noexcept : type((uint32_t)TypeCode::IPAddress_tc), param(0), uVal(ip.GetV4LittleEndian()) { }
explicit constexpr ExpressionValue(std::nullptr_t dummy) noexcept : type((uint32_t)TypeCode::None), param(0), uVal(0) { }
explicit ExpressionValue(DateTime t) noexcept : type((t.tim == 0) ? (uint32_t)TypeCode::None : (uint32_t)TypeCode::DateTime_tc) { Set56BitValue(t.tim); }
@@ -140,12 +113,16 @@ struct ExpressionValue
explicit ExpressionValue(const MacAddress& mac) noexcept;
ExpressionValue(SpecialType s, uint32_t u) noexcept : type((uint32_t)TypeCode::Special), param((uint32_t)s), uVal(u) { }
explicit ExpressionValue(StringHandle h) noexcept : type((uint32_t)TypeCode::HeapString), param(0), shVal(h) { }
+ explicit ExpressionValue(ArrayHandle h) noexcept : type((uint32_t)TypeCode::HeapArray), param(0), ahVal(h) { }
explicit ExpressionValue(const IoPort& p) noexcept : type((uint32_t)TypeCode::Port), param(0), iopVal(&p) { }
explicit ExpressionValue(const UniqueId& id) noexcept : type((uint32_t)TypeCode::UniqueId_tc), param(0), uniqueIdVal(&id) { }
#if SUPPORT_CAN_EXPANSION
ExpressionValue(const char*s, ExpansionDetail p) noexcept : type((uint32_t)TypeCode::CanExpansionBoardDetails), param((uint32_t)p), sVal(s) { }
#endif
+ bool operator==(const ExpressionValue& other) const noexcept;
+ bool operator!=(const ExpressionValue& other) const noexcept { return !operator==(other); }
+
ExpressionValue(const ExpressionValue& other) noexcept;
ExpressionValue(ExpressionValue&& other) noexcept;
~ExpressionValue();
@@ -154,18 +131,24 @@ struct ExpressionValue
TypeCode GetType() const noexcept { return (TypeCode)type; }
bool IsStringType() const noexcept { return type == (uint32_t)TypeCode::CString || type == (uint32_t)TypeCode::HeapString; }
+ bool IsHeapStringArrayType() const noexcept;
void SetBool(bool b) noexcept;
void SetInt(int32_t i) noexcept;
void SetFloat(float f, uint32_t digits) noexcept;
void SetFloat(float f) noexcept { SetFloat(f, MaxFloatDigitsDisplayedAfterPoint); }
void SetCString(const char *_ecv_array s) noexcept { Release(); type = (uint32_t)TypeCode::CString; sVal = s; }
+ void SetIPAddress(IPAddress ip) noexcept { Release(); type = (uint32_t)TypeCode::IPAddress_tc; uVal = ip.GetV4LittleEndian(); }
void SetDriverId(DriverId did) noexcept;
void SetStringHandle(StringHandle sh) noexcept { Release(); type = (uint32_t)TypeCode::HeapString; shVal = sh; }
+ void SetArrayHandle(ArrayHandle ah) noexcept { Release(); type = (uint32_t)TypeCode::HeapArray; ahVal = ah; }
void SetNull(std::nullptr_t dummy) noexcept { Release(); type = (uint32_t)TypeCode::None; }
void SetDateTime(time_t t) noexcept { Release(); type = (uint32_t)TypeCode::DateTime_tc; Set56BitValue(t); }
+ void SetUnsigned(uint32_t u) noexcept { Release(); type = (uint32_t)TypeCode::Uint32; uVal = u; }
+ void SetDuration(uint32_t u) noexcept { Release(); type = (uint32_t)TypeCode::Duration; uVal = u; }
+
// Store a 56-bit value
void Set56BitValue(uint64_t v) { param = (uint32_t)(v >> 32) & 0x00FFFFFFu; uVal = (uint32_t)v; }
@@ -213,6 +196,9 @@ public:
// Constructor used when evaluating expressions
ObjectExplorationContext(const GCodeBuffer *_ecv_null gbp, bool wal, bool wex, int p_line, int p_col) noexcept;
+ // Constructor used for generating a context to pass array indices
+ ObjectExplorationContext() noexcept;
+
const GCodeBuffer *_ecv_null GetGCodeBuffer() const noexcept { return gb; }
void SetMaxDepth(unsigned int d) noexcept { maxDepth = d; }
bool IncreaseDepth() noexcept { if (currentDepth < maxDepth) { ++currentDepth; return true; } return false; }
@@ -221,8 +207,8 @@ public:
void AddIndex() THROWS(GCodeException);
void RemoveIndex() THROWS(GCodeException);
void ProvideIndex(int32_t index) THROWS(GCodeException);
- int32_t GetIndex(size_t n) const THROWS(GCodeException);
- int32_t GetLastIndex() const THROWS(GCodeException);
+ int32_t GetIndex(size_t n) const noexcept;
+ int32_t GetLastIndex() const noexcept;
size_t GetNumIndicesCounted() const noexcept { return numIndicesCounted; }
unsigned int GetStartElement() const noexcept { return startElement; }
void SetNextElement(int arg) noexcept { nextElement = arg; }
@@ -270,7 +256,7 @@ private:
};
// Entry to describe an array of objects or values. These must be brace-initializable into flash memory.
-class ObjectModelArrayDescriptor
+class ObjectModelArrayTableEntry
{
public:
ReadWriteLock *null lockPointer;
@@ -287,6 +273,9 @@ public:
ObjectModel() noexcept;
virtual ~ObjectModel() { }
+ // Forwarding function so that we can make GetObjectModelArrayEntry() protected
+ const ObjectModelArrayTableEntry *FindObjectModelArrayEntry(unsigned int index) const noexcept { return GetObjectModelArrayEntry(index); }
+
// Construct a JSON representation of those parts of the object model requested by the user. This version is called only on the root of the tree.
void ReportAsJson(const GCodeBuffer *_ecv_null gb, OutputBuffer *buf, const char *_ecv_array filter, const char *_ecv_array reportFlags, bool wantArrayLength) const THROWS(GCodeException);
@@ -306,7 +295,10 @@ protected:
virtual void ReportAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char *_ecv_array filter) const THROWS(GCodeException);
// Report an entire array as JSON
- void ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor, const ObjectModelArrayDescriptor *omad, const char *_ecv_array filter) const THROWS(GCodeException);
+ void ReportObjectModelArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor, const ObjectModelArrayTableEntry *entry, const char *_ecv_array filter) const THROWS(GCodeException);
+
+ // Report an entire array as JSON
+ void ReportHeapArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor, ArrayHandle ah, const char *_ecv_array filter) const THROWS(GCodeException);
// Get the value of an object that we hold
ExpressionValue GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ExpressionValue& val, const char *_ecv_array idString) const THROWS(GCodeException);
@@ -316,10 +308,19 @@ protected:
virtual const ObjectModelClassDescriptor *GetObjectModelClassDescriptor() const noexcept = 0;
- __attribute__ ((noinline)) void ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *null classDescriptor,
- const ExpressionValue& val, const char *filter) const THROWS(GCodeException);
+ // Get the requested entry in the array table
+ virtual const ObjectModelArrayTableEntry *GetObjectModelArrayEntry(unsigned int index) const noexcept { return nullptr; }
+
+ // Return the address of the ReadWriteLock (if any) that we need to acquire before querying or reporting on this object
+ // Override this default implementation in classes that need to be locked. If the returned lock belongs to the current object
+ // then it must be declared 'mutable' because this function is const, like the querying and reporting functions
+ virtual ReadWriteLock *_ecv_null GetObjectLock(unsigned int tableNumber) const noexcept { return nullptr; }
+
private:
// These functions have been separated from ReportItemAsJson to avoid high stack usage in the recursive functions, therefore they must not be inlined
+ // Report on a single item
+ __attribute__ ((noinline)) void ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *_ecv_null classDescriptor,
+ const ExpressionValue& val, const char *filter) const THROWS(GCodeException);
__attribute__ ((noinline)) void ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ExpressionValue& val) const noexcept;
__attribute__ ((noinline)) static void ReportDateTime(OutputBuffer *buf, const ExpressionValue& val) noexcept;
__attribute__ ((noinline)) static void ReportFloat(OutputBuffer *buf, const ExpressionValue& val) noexcept;
@@ -334,6 +335,40 @@ private:
};
+// Function to report a value or object as JSON
+// This function is recursive, so keep its stack usage low.
+// Most recursive calls are for non-array object values, so handle object values inline to reduce stack usage.
+// This saves about 240 bytes of stack space but costs 272 bytes of flash memory.
+inline void ObjectModel::ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor,
+ const ExpressionValue& val, const char *_ecv_array filter) const THROWS(GCodeException)
+{
+ if (context.WantArrayLength() && *filter == 0)
+ {
+ ReportArrayLengthAsJson(buf, context, val);
+ }
+ else if (val.GetType() == TypeCode::ObjectModel_tc)
+ {
+ if ( (*filter != '.' && *filter != 0) // we should have reached the end of the filter or a '.', error if not
+ || val.omVal == nullptr // OM arrays may contain null entries, so we need to handle them here
+ )
+ {
+ buf->cat("null");
+ }
+ else
+ {
+ if (*filter == '.')
+ {
+ ++filter;
+ }
+ val.omVal->ReportAsJson(buf, context, (val.omVal == this) ? classDescriptor : nullptr, val.param, filter);
+ }
+ }
+ else
+ {
+ ReportItemAsJsonFull(buf, context, classDescriptor, val, filter);
+ }
+}
+
// Function used for compile-time check for the correct number of entries in an object model table
static inline constexpr size_t ArraySum(const uint8_t *_ecv_array arr, size_t numEntries) noexcept
{
@@ -364,7 +399,7 @@ public:
bool IsObsolete() const noexcept { return ((uint8_t)flags & (uint8_t)ObjectModelEntryFlags::obsolete) != 0; }
// See whether we should add the value of this element to the buffer, returning true if it matched the filter and we did add it
- bool ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModel *_ecv_from self, const char *_ecv_array filter, bool first) const noexcept;
+ bool ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModel *_ecv_from self, const char *_ecv_array filter, bool first) const THROWS(GCodeException);
// Return the name of this field
const char *_ecv_array GetName() const noexcept { return name; }
@@ -408,6 +443,11 @@ struct ObjectModelClassDescriptor
static const uint8_t objectModelTableDescriptor[]; \
static const ObjectModelClassDescriptor objectModelClassDescriptor;
+#define DECLARE_OBJECT_MODEL_WITH_ARRAYS \
+ DECLARE_OBJECT_MODEL \
+ static const ObjectModelArrayTableEntry objectModelArrayTable[]; \
+ const ObjectModelArrayTableEntry *GetObjectModelArrayEntry(unsigned int index) const noexcept override;
+
#define DECLARE_OBJECT_MODEL_VIRTUAL \
virtual const ObjectModelClassDescriptor *GetObjectModelClassDescriptor() const noexcept override = 0;
@@ -435,22 +475,51 @@ struct ObjectModelClassDescriptor
return &objectModelClassDescriptor; \
}
+#define DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(_class) \
+ constexpr unsigned int ArrayIndexOffset = 0; \
+ const ObjectModelArrayTableEntry *_class::GetObjectModelArrayEntry(unsigned int index) const noexcept \
+ { \
+ if (index < ARRAY_SIZE(_class::objectModelArrayTable)) \
+ { \
+ return &_class::objectModelArrayTable[index]; \
+ } \
+ return ObjectModel::GetObjectModelArrayEntry(index);; \
+ }
+
+#define DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE_WITH_PARENT(_class,_parent,_offset) \
+ constexpr unsigned int ArrayIndexOffset = _offset; \
+ const ObjectModelArrayTableEntry *_class::GetObjectModelArrayEntry(unsigned int index) const noexcept \
+ { \
+ if (index >= _offset && index < _offset + ARRAY_SIZE(_class::objectModelArrayTable)) \
+ { \
+ return &_class::objectModelArrayTable[index - _offset]; \
+ } \
+ return _parent::GetObjectModelArrayEntry(index); \
+ }
+
#define OBJECT_MODEL_FUNC_BODY(_class,...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
{ const _class * const self = static_cast<const _class*>(arg); return ExpressionValue(__VA_ARGS__); }
#define OBJECT_MODEL_FUNC_IF_BODY(_class,_condition,...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
{ const _class * const self = static_cast<const _class*>(arg); return (_condition) ? ExpressionValue(__VA_ARGS__) : ExpressionValue(nullptr); }
+#define OBJECT_MODEL_FUNC_ARRAY(_index) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
+ { \
+ static_assert((unsigned int)_index >= ArrayIndexOffset); \
+ static_assert((unsigned int)_index < sizeof(objectModelArrayTable)/sizeof(ObjectModelArrayTableEntry) + ArrayIndexOffset); \
+ return ExpressionValue(arg, _index, true); \
+ }
#define OBJECT_MODEL_FUNC_NOSELF(...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept { return ExpressionValue(__VA_ARGS__); }
#define OBJECT_MODEL_FUNC_IF_NOSELF(_condition,...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
{ return (_condition) ? ExpressionValue(__VA_ARGS__) : ExpressionValue(nullptr); }
-#define OBJECT_MODEL_ARRAY(_name) static const ObjectModelArrayDescriptor _name ## ArrayDescriptor;
#else
-#define INHERIT_OBJECT_MODEL // nothing
-#define DECLARE_OBJECT_MODEL // nothing
-#define DECLARE_OBJECT_MODEL_VIRTUAL // nothing
-#define DEFINE_GET_OBJECT_MODEL_TABLE // nothing
-#define OBJECT_MODEL_ARRAY(_name) // nothing
+#define INHERIT_OBJECT_MODEL // nothing
+#define DECLARE_OBJECT_MODEL // nothing
+#define DECLARE_OBJECT_MODEL_WITH_ARRAYS // nothing
+#define DECLARE_OBJECT_MODEL_VIRTUAL // nothing
+#define DEFINE_GET_OBJECT_MODEL_TABLE(_class) // nothing
+#define DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(_class) // nothing
+#define DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(_class, _parent) // nothing
#endif
diff --git a/src/ObjectModel/TypeCode.h b/src/ObjectModel/TypeCode.h
new file mode 100644
index 00000000..5720c63d
--- /dev/null
+++ b/src/ObjectModel/TypeCode.h
@@ -0,0 +1,46 @@
+/*
+ * TypeCode.h
+ *
+ * Created on: 27 Sept 2022
+ * Author: David
+ */
+
+#ifndef SRC_OBJECTMODEL_TYPECODE_H_
+#define SRC_OBJECTMODEL_TYPECODE_H_
+
+#include <cstdint>
+
+// Type codes to indicate what type of expression we have and how it is represented.
+// The "Special" type is for items that we have to evaluate when we are ready to write them out, in particular strings whose storage might disappear.
+enum class TypeCode : uint8_t
+{
+ None = 0,
+ Bool,
+ Char,
+ Uint32,
+ Int32,
+ Uint64, // only 56 bits actually available
+ Float,
+ Bitmap16,
+ Bitmap32,
+ Bitmap64, // only 56 bits actually available
+ Enum32,
+ ObjectModel_tc, // renamed for eCv to avoid clash with class ObjectModel
+ CString,
+ HeapString,
+ HeapArray,
+ IPAddress_tc, // renamed for eCv to avoid clash with class IPAddress in RRFLibraries
+ ObjectModelArray,
+ DateTime_tc, // renamed for eCv to avoid clash with class DateTime
+ DriverId_tc, // renamed for eCv to avoid clash with class DriverId
+ MacAddress_tc, // renamed for eCv to avoid clash with class MacAddress
+ Special,
+ Port,
+ UniqueId_tc,
+ Duration, // a duration represented an unsigned number of seconds (used by the 12864 LCD code)
+#if SUPPORT_CAN_EXPANSION
+ CanExpansionBoardDetails
+#endif
+};
+
+#endif /* SRC_OBJECTMODEL_TYPECODE_H_ */
diff --git a/src/ObjectModel/Variable.cpp b/src/ObjectModel/Variable.cpp
index b9bb2f09..537e1448 100644
--- a/src/ObjectModel/Variable.cpp
+++ b/src/ObjectModel/Variable.cpp
@@ -7,9 +7,12 @@
#include "Variable.h"
#include <Platform/OutputMemory.h>
+#include <GCodes/GCodeBuffer/GCodeBuffer.h>
-Variable::Variable(const char *str, ExpressionValue pVal, int8_t pScope) noexcept : name(str), val(pVal), scope(pScope)
+Variable::Variable(const char *str, ExpressionValue& pVal, int8_t pScope) THROWS(GCodeException)
+ : name(str), val(), scope(pScope)
{
+ Assign(pVal); // this may throw
}
Variable::~Variable()
@@ -18,6 +21,42 @@ Variable::~Variable()
val.Release();
}
+void Variable::Assign(ExpressionValue& ev) THROWS(GCodeException)
+{
+ switch (ev.GetType())
+ {
+ case TypeCode::ObjectModelArray:
+ {
+ // Copy the object model array value to the heap
+ ArrayHandle ah;
+ const ObjectModelArrayTableEntry *const entry = ev.omVal->FindObjectModelArrayEntry(ev.param);
+ ReadLocker lock(entry->lockPointer);
+ ObjectExplorationContext context;
+ const size_t numElements = entry->GetNumElements(ev.omVal, context);
+ if (numElements != 0)
+ {
+ ah.Allocate(numElements);
+ for (size_t i = 0; i < numElements; ++i)
+ {
+ context.AddIndex(i);
+ ExpressionValue elemVal = entry->GetElement(ev.omVal, context);
+ ah.AssignElement(i, elemVal);
+ context.RemoveIndex();
+ }
+ }
+ val = ExpressionValue(ah);
+ }
+ break;
+
+ case TypeCode::ObjectModel_tc:
+ throw GCodeException("Cannot assign a value of type 'object' to a variable");
+
+ default:
+ val = ev;
+ break;
+ }
+}
+
Variable* VariableSet::Lookup(const char *str) noexcept
{
LinkedVariable *lv;
@@ -32,13 +71,13 @@ Variable* VariableSet::Lookup(const char *str) noexcept
return nullptr;
}
-const Variable* VariableSet::Lookup(const char *str) const noexcept
+const Variable* VariableSet::Lookup(const char *str, size_t length) const noexcept
{
const LinkedVariable *lv;
for (lv = root; lv != nullptr; lv = lv->next)
{
auto vname = lv->v.GetName();
- if (strcmp(vname.Ptr(), str) == 0)
+ if (strlen(vname.Ptr()) == length && memcmp(vname.Ptr(), str, length) == 0)
{
return &(lv->v);
}
@@ -46,7 +85,7 @@ const Variable* VariableSet::Lookup(const char *str) const noexcept
return nullptr;
}
-void VariableSet::InsertNew(const char *str, ExpressionValue pVal, int8_t pScope) noexcept
+void VariableSet::InsertNew(const char *str, ExpressionValue pVal, int8_t pScope) THROWS(GCodeException)
{
LinkedVariable * const toInsert = new LinkedVariable(str, pVal, pScope, root);
root = toInsert;
diff --git a/src/ObjectModel/Variable.h b/src/ObjectModel/Variable.h
index 6f951d04..fd5421a4 100644
--- a/src/ObjectModel/Variable.h
+++ b/src/ObjectModel/Variable.h
@@ -13,18 +13,19 @@
#include <Platform/Heap.h>
#include <ObjectModel/ObjectModel.h>
#include <General/function_ref.h>
+#include <GCodes/GCodeException.h>
// Class to represent a variable having a name and a value
class Variable
{
public:
- Variable(const char *_ecv_array str, ExpressionValue pVal, int8_t pScope) noexcept;
+ Variable(const char *_ecv_array str, ExpressionValue& pVal, int8_t pScope) THROWS(GCodeException);
~Variable();
ReadLockedPointer<const char> GetName() const noexcept { return name.Get(); }
ExpressionValue GetValue() const noexcept { return val; }
int8_t GetScope() const noexcept { return scope; }
- void Assign(ExpressionValue ev) noexcept { val = ev; }
+ void Assign(ExpressionValue& ev) THROWS(GCodeException);
private:
StringHandle name;
@@ -45,9 +46,9 @@ public:
void AssignFrom(VariableSet& other) noexcept;
Variable *Lookup(const char *_ecv_array str) noexcept;
- const Variable *Lookup(const char *_ecv_array str) const noexcept;
- void InsertNew(const char *str, ExpressionValue pVal, int8_t pScope) noexcept;
- void InsertNewParameter(const char *str, ExpressionValue pVal) noexcept { InsertNew(str, pVal, -1); }
+ const Variable *Lookup(const char *_ecv_array str, size_t length) const noexcept pre(length <= strlen(str));
+ void InsertNew(const char *str, ExpressionValue pVal, int8_t pScope) THROWS(GCodeException);
+ void InsertNewParameter(const char *str, ExpressionValue pVal) THROWS(GCodeException) { InsertNew(str, pVal, -1); }
void EndScope(uint8_t blockNesting) noexcept;
void Delete(const char *str) noexcept;
void Clear() noexcept;
@@ -59,7 +60,8 @@ private:
{
DECLARE_FREELIST_NEW_DELETE(LinkedVariable)
- LinkedVariable(const char *_ecv_array str, ExpressionValue pVal, int8_t pScope, LinkedVariable *p_next) : next(p_next), v(str, pVal, pScope) {}
+ LinkedVariable(const char *_ecv_array str, ExpressionValue pVal, int8_t pScope, LinkedVariable *p_next) THROWS(GCodeException)
+ : next(p_next), v(str, pVal, pScope) {}
LinkedVariable * null next;
Variable v;
diff --git a/src/Platform/ArrayHandle.cpp b/src/Platform/ArrayHandle.cpp
new file mode 100644
index 00000000..c58929e2
--- /dev/null
+++ b/src/Platform/ArrayHandle.cpp
@@ -0,0 +1,152 @@
+/*
+ * ArrayHandle.cpp
+ *
+ * Created on: 12 Jun 2022
+ * Author: David
+ */
+
+#include "ArrayHandle.h"
+#include <ObjectModel/ObjectModel.h>
+
+// Declare placement new operator for class ExpressionValue so that we don't have to include <new>
+void* operator new(std::size_t, ExpressionValue* arg) noexcept { return arg; }
+
+struct ArrayStorageSpace
+{
+ uint16_t length; // length of this object in bytes including this length field, always rounded up to a multiple of 4
+ uint16_t count; // number of elements in the array
+ ExpressionValue elements[]; // the array elements
+};
+
+// Allocate space for an array
+void ArrayHandle::Allocate(size_t numElements) THROWS(GCodeException)
+{
+ WriteLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ Heap::IndexSlot * const slot = Heap::AllocateHandle();
+ Heap::StorageSpace * const space = Heap::AllocateSpace(sizeof(ArrayStorageSpace) + numElements * sizeof(ExpressionValue));
+ slot->storage = space;
+ if (space->length < sizeof(ArrayStorageSpace) + numElements * sizeof(ExpressionValue))
+ {
+ Heap::DeleteSlot(slot);
+ throw GCodeException("Array too large");
+ }
+
+ ArrayStorageSpace * const aSpace = reinterpret_cast<ArrayStorageSpace*>(space);
+ aSpace->count = numElements;
+ for (size_t i = 0; i < numElements; ++i)
+ {
+ new (&aSpace->elements[i]) ExpressionValue;
+ }
+ slotPtr = slot;
+}
+
+void ArrayHandle::AssignElement(size_t index, ExpressionValue &val) THROWS(GCodeException)
+{
+ if (slotPtr != nullptr)
+ {
+ WriteLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ ArrayStorageSpace * const aSpace = reinterpret_cast<ArrayStorageSpace*>(slotPtr->storage);
+ if (index < aSpace->count)
+ {
+ aSpace->elements[index] = val;
+ return;
+ }
+ }
+ throw GCodeException("Array index out of bounds");
+}
+
+size_t ArrayHandle::GetNumElements() const noexcept
+{
+ if (slotPtr == nullptr)
+ {
+ return 0;
+ }
+ ReadLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ return reinterpret_cast<const ArrayStorageSpace*>(slotPtr->storage)->count;
+}
+
+void ArrayHandle::GetElement(size_t index, ExpressionValue &rslt) const THROWS(GCodeException)
+{
+ if (slotPtr != nullptr)
+ {
+ ReadLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ ArrayStorageSpace * const aSpace = reinterpret_cast<ArrayStorageSpace*>(slotPtr->storage);
+ if (index < aSpace->count)
+ {
+ rslt = aSpace->elements[index];
+ return;
+ }
+ }
+ throw GCodeException("Array index out of bounds");
+}
+
+TypeCode ArrayHandle::GetElementType(size_t index) const noexcept
+{
+ if (slotPtr != nullptr)
+ {
+ ReadLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ ArrayStorageSpace * const aSpace = reinterpret_cast<ArrayStorageSpace*>(slotPtr->storage);
+ if (index < aSpace->count)
+ {
+ return aSpace->elements[index].GetType();
+ }
+ }
+ return TypeCode::None;
+}
+
+// Decrease the reference count for the array referred to and delete it if it reaches zero
+void ArrayHandle::Delete() noexcept
+{
+ if (slotPtr != nullptr)
+ {
+ ReadLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ ArrayStorageSpace *const aSpace = reinterpret_cast<ArrayStorageSpace*>(slotPtr->storage);
+ for (size_t i = 0; i < aSpace->count; ++i)
+ {
+ aSpace->elements[i].~ExpressionValue(); // call destructor on the elements
+ }
+ Heap::DeleteSlot(slotPtr);
+ slotPtr = nullptr; // clear the pointer to the handle entry
+ }
+}
+
+const ArrayHandle& ArrayHandle::IncreaseRefCount() const noexcept
+{
+ if (slotPtr != nullptr)
+ {
+ Heap::IncreaseRefCount(slotPtr);
+ }
+ return *this;
+}
+
+// AutoArrayHandle members
+
+AutoArrayHandle::AutoArrayHandle(const AutoArrayHandle &other) noexcept
+ : ArrayHandle(other)
+{
+ IncreaseRefCount();
+}
+
+AutoArrayHandle::AutoArrayHandle(AutoArrayHandle &&other) noexcept
+ : ArrayHandle(other)
+{
+ other.slotPtr = nullptr;
+}
+
+AutoArrayHandle& AutoArrayHandle::operator =(const AutoArrayHandle &other) noexcept
+{
+ if (slotPtr != other.slotPtr)
+ {
+ Delete();
+ slotPtr = other.slotPtr;
+ IncreaseRefCount();
+ }
+ return *this;
+}
+
+AutoArrayHandle::~AutoArrayHandle()
+{
+ ArrayHandle::Delete();
+}
+
+// End
diff --git a/src/Platform/ArrayHandle.h b/src/Platform/ArrayHandle.h
new file mode 100644
index 00000000..da075d4c
--- /dev/null
+++ b/src/Platform/ArrayHandle.h
@@ -0,0 +1,50 @@
+/*
+ * ArrayHandle.h
+ *
+ * Created on: 12 Jun 2022
+ * Author: David
+ */
+
+#ifndef SRC_PLATFORM_ARRAYHANDLE_H_
+#define SRC_PLATFORM_ARRAYHANDLE_H_
+
+#include <RepRapFirmware.h>
+#include <ObjectModel/TypeCode.h>
+#include "Heap.h"
+
+class ExpressionValue;
+
+// Note: ArrayHandle is a union member in ExpressionValue, therefore it must be no larger than 32 bits and it cannot have a non-trivial destructor, copy constructor etc.
+// This means that when an object containing an ArrayHandle is copied or destroyed, that object must handle the reference count.
+// Classes other than ExpressionValue should use AutoArrayHandle instead;
+class ArrayHandle
+{
+public:
+ ArrayHandle() noexcept { slotPtr = nullptr; } // build an empty array
+
+ void Allocate(size_t numElements) THROWS(GCodeException);
+ void AssignElement(size_t index, ExpressionValue& val) THROWS(GCodeException);
+
+ size_t GetNumElements() const noexcept; // get the number of elements
+ void GetElement(size_t index, ExpressionValue& rslt) const THROWS(GCodeException); // get the specified element
+ TypeCode GetElementType(size_t index) const noexcept;
+ void Delete() noexcept;
+ const ArrayHandle& IncreaseRefCount() const noexcept;
+ bool IsNull() const noexcept { return slotPtr == nullptr; }
+
+protected:
+ Heap::IndexSlot * null slotPtr;
+};
+
+// Version of ArrayHandle that updates the reference counts automatically
+class AutoArrayHandle : public ArrayHandle
+{
+public:
+ AutoArrayHandle() noexcept : ArrayHandle() { }
+ AutoArrayHandle(const AutoArrayHandle& other) noexcept;
+ AutoArrayHandle(AutoArrayHandle&& other) noexcept;
+ AutoArrayHandle& operator=(const AutoArrayHandle& other) noexcept;
+ ~AutoArrayHandle();
+};
+
+#endif /* SRC_PLATFORM_ARRAYHANDLE_H_ */
diff --git a/src/Platform/Heap.cpp b/src/Platform/Heap.cpp
index fb819222..c53995f0 100644
--- a/src/Platform/Heap.cpp
+++ b/src/Platform/Heap.cpp
@@ -16,68 +16,59 @@
#include <General/String.h>
#include <atomic>
-#define CHECK_HANDLES (1) // set nonzero to check that handles are valid before dereferencing them
-
constexpr size_t IndexBlockSlots = 99; // number of 4-byte handles per index block, plus one for link to next index block
constexpr size_t HeapBlockSize = 2048; // the size of each heap block
-struct StorageSpace
+namespace Heap
{
- uint16_t length;
- char data[]; // array of unspecified length at the end of a struct is a GNU extension (valid in C but not valid in standard C++)
-};
+ struct IndexBlock
+ {
+ void* operator new(size_t count) { return Tasks::AllocPermanent(count); }
+ void* operator new(size_t count, std::align_val_t align) { return Tasks::AllocPermanent(count, align); }
+ void operator delete(void* ptr) noexcept {}
+ void operator delete(void* ptr, std::align_val_t align) noexcept {}
-struct IndexSlot
-{
- StorageSpace *storage;
- std::atomic<unsigned int> refCount;
+ IndexBlock() noexcept : next(nullptr) { }
- IndexSlot() noexcept : storage(nullptr), refCount(0) { }
-};
+ IndexBlock *next;
+ IndexSlot slots[IndexBlockSlots];
+ };
-struct IndexBlock
-{
- void* operator new(size_t count) { return Tasks::AllocPermanent(count); }
- void* operator new(size_t count, std::align_val_t align) { return Tasks::AllocPermanent(count, align); }
- void operator delete(void* ptr) noexcept {}
- void operator delete(void* ptr, std::align_val_t align) noexcept {}
-
- IndexBlock() noexcept : next(nullptr) { }
+ struct HeapBlock
+ {
+ void* operator new(size_t count) { return Tasks::AllocPermanent(count); }
+ void* operator new(size_t count, std::align_val_t align) { return Tasks::AllocPermanent(count, align); }
+ void operator delete(void* ptr) noexcept {}
+ void operator delete(void* ptr, std::align_val_t align) noexcept {}
+
+ HeapBlock(HeapBlock *pNext) noexcept : next(pNext), allocated(0) { }
+ HeapBlock *next;
+ size_t allocated;
+ char data[HeapBlockSize];
+ };
+
+ void GarbageCollectInternal() noexcept;
+ void AdjustHandles(char *startAddr, char *endAddr, size_t moveDown, unsigned int numHandles) noexcept;
+
+ IndexBlock *indexRoot = nullptr;
+ HeapBlock *heapRoot = nullptr;
+ size_t handlesAllocated = 0;
+ std::atomic<size_t> handlesUsed = 0;
+ size_t heapAllocated = 0;
+ size_t heapUsed = 0;
+ std::atomic<size_t> heapToRecycle = 0;
+ unsigned int gcCyclesDone = 0;
+}
- IndexBlock *next;
- IndexSlot slots[IndexBlockSlots];
-};
+ReadWriteLock Heap::heapLock;
-struct HeapBlock
-{
- void* operator new(size_t count) { return Tasks::AllocPermanent(count); }
- void* operator new(size_t count, std::align_val_t align) { return Tasks::AllocPermanent(count, align); }
- void operator delete(void* ptr) noexcept {}
- void operator delete(void* ptr, std::align_val_t align) noexcept {}
-
- HeapBlock(HeapBlock *pNext) noexcept : next(pNext), allocated(0) { }
- HeapBlock *next;
- size_t allocated;
- char data[HeapBlockSize];
-};
-
-ReadWriteLock StringHandle::heapLock;
-IndexBlock *StringHandle::indexRoot = nullptr;
-HeapBlock *StringHandle::heapRoot = nullptr;
-size_t StringHandle::handlesAllocated = 0;
-std::atomic<size_t> StringHandle::handlesUsed = 0;
-size_t StringHandle::heapAllocated = 0;
-size_t StringHandle::heapUsed = 0;
-std::atomic<size_t> StringHandle::heapToRecycle = 0;
-unsigned int StringHandle::gcCyclesDone = 0;
-
-/*static*/ void StringHandle::GarbageCollect() noexcept
+void Heap::GarbageCollect() noexcept
{
WriteLocker locker(heapLock);
GarbageCollectInternal();
}
-/*static*/ void StringHandle::GarbageCollectInternal() noexcept
+void Heap::GarbageCollectInternal() noexcept
{
#if CHECK_HANDLES
RRF_ASSERT(heapLock.GetWriteLockOwner() == TaskBase::GetCallerTaskHandle());
@@ -95,7 +86,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
{
break;
}
- p += len + sizeof(StorageSpace::length);
+ p += len;
}
if (p < currentBlock->data + currentBlock->allocated) // if we found an unused block before we reached the end
@@ -112,7 +103,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
{
break;
}
- p += (len & ~1u) + sizeof(StorageSpace::length);
+ p += (len & ~1u);
}
if (p >= currentBlock->data + currentBlock->allocated)
@@ -133,7 +124,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
break;
}
++numHandlesToAdjust;
- p += len + sizeof(StorageSpace::length);
+ p += len;
}
// Move the contiguous blocks down
@@ -154,7 +145,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
}
// Find all handles pointing to storage between startAddr and endAddr and move the pointers down by amount moveDown
-/*static*/ void StringHandle::AdjustHandles(char *startAddr, char *endAddr, size_t moveDown, unsigned int numHandles) noexcept
+void Heap::AdjustHandles(char *startAddr, char *endAddr, size_t moveDown, unsigned int numHandles) noexcept
{
for (IndexBlock *indexBlock = indexRoot; indexBlock != nullptr; indexBlock = indexBlock->next)
{
@@ -174,7 +165,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
}
}
-/*static*/ bool StringHandle::CheckIntegrity(const StringRef& errmsg) noexcept
+bool Heap::CheckIntegrity(const StringRef& errmsg) noexcept
{
ReadLocker lock(heapLock);
@@ -190,7 +181,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
++numHeapErrors;
break;
}
- p += (reinterpret_cast<const StorageSpace*>(p)->length & (~1u)) + sizeof(StorageSpace::length);
+ p += (reinterpret_cast<const StorageSpace*>(p)->length & (~1u));
}
}
@@ -227,7 +218,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
}
break;
}
- p += (reinterpret_cast<const StorageSpace*>(p)->length & (~1u)) + sizeof(StorageSpace::length);
+ p += (reinterpret_cast<const StorageSpace*>(p)->length & (~1u));
}
break;
}
@@ -251,8 +242,8 @@ unsigned int StringHandle::gcCyclesDone = 0;
return true;
}
-// Allocate a new handle. Must own the write lock when calling this.
-/*static*/ IndexSlot *StringHandle::AllocateHandle() noexcept
+// Allocate a new handle and give it a reference count of 1. Must own the write lock when calling this.
+Heap::IndexSlot *Heap::AllocateHandle() noexcept
{
#if CHECK_HANDLES
RRF_ASSERT(heapLock.GetWriteLockOwner() == TaskBase::GetCallerTaskHandle());
@@ -266,7 +257,7 @@ unsigned int StringHandle::gcCyclesDone = 0;
{
if (curBlock->slots[i].storage == nullptr)
{
- curBlock->slots[i].refCount = 0;
+ curBlock->slots[i].refCount = 1;
++handlesUsed;
return &curBlock->slots[i];
}
@@ -289,29 +280,30 @@ unsigned int StringHandle::gcCyclesDone = 0;
}
++handlesUsed;
+ newIndexBlock->slots[0].refCount = 1;
return &newIndexBlock->slots[0];
}
// Allocate the requested space. If 'length' is above the maximum supported size, it will be truncated.
-/*static*/ StorageSpace *StringHandle::AllocateSpace(size_t length) noexcept
+Heap::StorageSpace *Heap::AllocateSpace(size_t length) noexcept
{
#if CHECK_HANDLES
RRF_ASSERT(heapLock.GetWriteLockOwner() == TaskBase::GetCallerTaskHandle());
#endif
- length = min<size_t>((length + 1) & (~1u), HeapBlockSize - sizeof(StorageSpace::length)); // round to an even length to keep things aligned and limit to max size
+ length = min<size_t>((length + 3u) & (~3u), HeapBlockSize); // round to make the length field a multiple of 4 and limit to max size
bool collected = false;
do
{
for (HeapBlock *currentBlock = heapRoot; currentBlock != nullptr; currentBlock = currentBlock->next)
{
- if (HeapBlockSize - sizeof(StorageSpace::length) >= currentBlock->allocated + length) // if the data will fit at the end of the current block
+ if (HeapBlockSize >= currentBlock->allocated + length) // if the data will fit at the end of the current block
{
StorageSpace * const ret = reinterpret_cast<StorageSpace*>(currentBlock->data + currentBlock->allocated);
ret->length = length;
- currentBlock->allocated += length + sizeof(StorageSpace::length);
- heapUsed += length + sizeof(StorageSpace::length);
+ currentBlock->allocated += length;
+ heapUsed += length;
return ret;
}
}
@@ -328,119 +320,16 @@ unsigned int StringHandle::gcCyclesDone = 0;
// Create a new heap block
heapRoot = new HeapBlock(heapRoot);
heapAllocated += HeapBlockSize;
- heapUsed += length + sizeof(StorageSpace::length);
+ heapUsed += length;
StorageSpace * const ret2 = reinterpret_cast<StorageSpace*>(heapRoot->data);
ret2->length = length;
- heapRoot->allocated = length + sizeof(StorageSpace::length);
+ heapRoot->allocated = length;
return ret2;
}
-// StringHandle members
-// Build a handle from a single null-terminated string
-StringHandle::StringHandle(const char *s) noexcept : StringHandle(s, strlen(s)) { }
-
-// Build a handle from a character array and a length
-StringHandle::StringHandle(const char *s, size_t len) noexcept
-{
- if (len == 0)
- {
- slotPtr = nullptr;
- }
- else
- {
- WriteLocker locker(heapLock); // prevent other tasks modifying the heap
- InternalAssign(s, len);
- }
-}
-
-#if 0 // This constructor is currently unused, but may be useful in future
-// Build a handle by concatenating two strings
-StringHandle::StringHandle(const char *s1, const char *s2) noexcept
-{
- const size_t len = strlen(s1) + strlen(s2);
- if (len == 0)
- {
- slotPtr = nullptr;
- }
- else
- {
- WriteLocker locker(heapLock); // prevent other tasks modifying the heap
- IndexSlot * const slot = AllocateHandle();
- StorageSpace * const space = AllocateSpace(len + 1);
- SafeStrncpy(space->data, s1, space->length);
- SafeStrncat(space->data, s2, space->length);
- slot->storage = space;
- slot->refCount = 1;
- slotPtr = slot;
- }
-}
-#endif
-
-void StringHandle::Assign(const char *s) noexcept
+// Check that the handle points into an index block
+void Heap::CheckSlotGood(IndexSlot *slotPtr) noexcept
{
- Delete();
- const size_t len = strlen(s);
- if (len != 0)
- {
- WriteLocker locker(heapLock); // prevent other tasks modifying the heap
- InternalAssign(s, len);
- }
-}
-
-void StringHandle::InternalAssign(const char *s, size_t len) noexcept
-{
- IndexSlot * const slot = AllocateHandle();
- StorageSpace * const space = AllocateSpace(len + 1);
- SafeStrncpy(space->data, s, len + 1);
- slot->storage = space;
- slot->refCount = 1;
- slotPtr = slot;
-}
-
-void StringHandle::Delete() noexcept
-{
- if (slotPtr != nullptr)
- {
- ReadLocker locker(heapLock); // prevent other tasks modifying the heap
- InternalDelete();
- }
-}
-
-const StringHandle& StringHandle::IncreaseRefCount() const noexcept
-{
- if (slotPtr != nullptr)
- {
- ++slotPtr->refCount;
- }
- return *this;
-}
-
-// Caller must have at least a read lock when calling this
-void StringHandle::InternalDelete() noexcept
-{
- RRF_ASSERT(slotPtr->refCount != 0);
- RRF_ASSERT(slotPtr->storage != nullptr);
- if (--slotPtr->refCount == 0)
- {
- heapToRecycle += slotPtr->storage->length + sizeof(StorageSpace::length);
- slotPtr->storage->length |= 1; // flag the space as unused
- slotPtr->storage = nullptr; // release the handle entry
- --handlesUsed;
- }
- slotPtr = nullptr; // clear the pointer to the handle entry
-}
-
-ReadLockedPointer<const char> StringHandle::Get() const noexcept
-{
- if (slotPtr == nullptr)
- {
- return ReadLockedPointer<const char>(nullptr, ""); // a null handle means an empty string
- }
-
- ReadLocker locker(heapLock);
-
-#if CHECK_HANDLES
- // Check that the handle points into an index block
RRF_ASSERT(((uint32_t)slotPtr & 3) == 0);
bool ok = false;
for (IndexBlock *indexBlock = indexRoot; indexBlock != nullptr; indexBlock = indexBlock->next)
@@ -453,40 +342,27 @@ ReadLockedPointer<const char> StringHandle::Get() const noexcept
}
RRF_ASSERT(ok);
RRF_ASSERT(slotPtr->refCount != 0);
-#endif
-
- return ReadLockedPointer<const char>(locker, slotPtr->storage->data);
}
-size_t StringHandle::GetLength() const noexcept
+void Heap::IncreaseRefCount(IndexSlot *slotPtr) noexcept
{
- if (slotPtr == nullptr)
- {
- return 0;
- }
-
- ReadLocker locker(heapLock);
+ ++slotPtr->refCount;
+}
-#if CHECK_HANDLES
- // Check that the handle points into an index block and is not null
- RRF_ASSERT(((uint32_t)slotPtr & 3) == 0);
- bool ok = false;
- for (IndexBlock *indexBlock = indexRoot; indexBlock != nullptr; indexBlock = indexBlock->next)
+void Heap::DeleteSlot(IndexSlot *slotPtr) noexcept
+{
+ RRF_ASSERT(slotPtr->refCount != 0);
+ RRF_ASSERT(slotPtr->storage != nullptr);
+ if (--slotPtr->refCount == 0)
{
- if (slotPtr >= &indexBlock->slots[0] && slotPtr < &indexBlock->slots[IndexBlockSlots])
- {
- ok = true;
- break;
- }
+ heapToRecycle += slotPtr->storage->length;
+ slotPtr->storage->length |= 1; // flag the space as unused
+ slotPtr->storage = nullptr; // release the handle entry
+ --handlesUsed;
}
- RRF_ASSERT(ok);
- RRF_ASSERT(slotPtr->refCount != 0);
-#endif
-
- return strlen(slotPtr->storage->data);
}
-/*static*/ void StringHandle::Diagnostics(MessageType mt, Platform& p) noexcept
+void Heap::Diagnostics(MessageType mt, Platform& p) noexcept
{
String<StringLength256> temp;
const bool ok = CheckIntegrity(temp.GetRef());
@@ -499,32 +375,4 @@ size_t StringHandle::GetLength() const noexcept
p.Message(mt, temp.c_str());
}
-// AutoStringHandle members
-
-AutoStringHandle::AutoStringHandle(const AutoStringHandle& other) noexcept
-{
- IncreaseRefCount();
-}
-
-AutoStringHandle::AutoStringHandle(AutoStringHandle&& other) noexcept
-{
- other.slotPtr = nullptr;
-}
-
-AutoStringHandle& AutoStringHandle::operator=(const AutoStringHandle& other) noexcept
-{
- if (slotPtr != other.slotPtr)
- {
- Delete();
- slotPtr = other.slotPtr;
- IncreaseRefCount();
- }
- return *this;
-}
-
-AutoStringHandle::~AutoStringHandle()
-{
- StringHandle::Delete();
-}
-
// End
diff --git a/src/Platform/Heap.h b/src/Platform/Heap.h
index 5f22993f..3ea05780 100644
--- a/src/Platform/Heap.h
+++ b/src/Platform/Heap.h
@@ -13,72 +13,34 @@
#include <atomic>
-class IndexSlot;
-class StorageSpace;
-class HeapBlock;
-class IndexBlock;
+#define CHECK_HANDLES (1) // set nonzero to check that handles are valid before dereferencing them
-// Note: StringHandle is a union member in ExpressionValue, therefore it cannot have a non-trivial destructor, copy constructor etc.
-// This means that when an object containing a StringHandle is copied or destroyed, that object must handle the reference count.
-// Classes other than ExpressionValue should use AutoStringHandle instead;
-class StringHandle
+namespace Heap
{
-public:
- StringHandle() noexcept { slotPtr = nullptr; }
- explicit StringHandle(const char *s) noexcept;
- StringHandle(const char *s, size_t len) noexcept;
-
-#if 0 // unused
- StringHandle(const char *s1, const char *s2) noexcept;
-#endif
-
- ReadLockedPointer<const char> Get() const noexcept;
- size_t GetLength() const noexcept;
- void Delete() noexcept;
- const StringHandle& IncreaseRefCount() const noexcept;
- bool IsNull() const noexcept { return slotPtr == nullptr; }
- void Assign(const char *s) noexcept;
-
- static void GarbageCollect() noexcept;
-// static size_t GetWastedSpace() noexcept { return spaceToRecycle; }
-// static size_t GetIndexSpace() noexcept { return totalIndexSpace; }
-// static size_t GetHeapSpace() noexcept { return totalHeapSpace; }
- static bool CheckIntegrity(const StringRef& errmsg) noexcept;
- static void Diagnostics(MessageType mt, Platform& p) noexcept;
-
-protected:
- void InternalAssign(const char *s, size_t len) noexcept;
- void InternalDelete() noexcept;
-
- static IndexSlot *AllocateHandle() noexcept;
- static StorageSpace *AllocateSpace(size_t length) noexcept;
- static void GarbageCollectInternal() noexcept;
- static void AdjustHandles(char *startAddr, char *endAddr, size_t moveDown, unsigned int numHandles) noexcept;
-
- IndexSlot * null slotPtr;
-
- static ReadWriteLock heapLock;
- static IndexBlock *indexRoot;
- static HeapBlock *heapRoot;
- static size_t handlesAllocated;
- static std::atomic<size_t> handlesUsed;
- static size_t heapAllocated;
- static size_t heapUsed;
- static std::atomic<size_t> heapToRecycle;
- static unsigned int gcCyclesDone;
-};
-
-// Version of StringHandle that updates the reference counts automatically
-class AutoStringHandle : public StringHandle
-{
-public:
- AutoStringHandle() noexcept : StringHandle() { }
- explicit AutoStringHandle(const char *s) noexcept : StringHandle(s) { }
- AutoStringHandle(const char *s, size_t len) noexcept : StringHandle(s, len) { }
- AutoStringHandle(const AutoStringHandle& other) noexcept;
- AutoStringHandle(AutoStringHandle&& other) noexcept;
- AutoStringHandle& operator=(const AutoStringHandle& other) noexcept;
- ~AutoStringHandle();
-};
+ struct StorageSpace
+ {
+ uint16_t length; // length of this object in bytes including this length field, always rounded up to a multiple of 4
+ char data[]; // array of unspecified length at the end of a struct is a GNU extension (valid in C but not valid in standard C++)
+ };
+
+ struct IndexSlot
+ {
+ StorageSpace *null storage;
+ std::atomic<unsigned int> refCount;
+
+ IndexSlot() noexcept : storage(nullptr), refCount(0) { }
+ };
+
+ IndexSlot *AllocateHandle() noexcept;
+ StorageSpace *AllocateSpace(size_t length) noexcept;
+ void CheckSlotGood(IndexSlot *slotPtr) noexcept;
+ void IncreaseRefCount(IndexSlot *slotPtr) noexcept;
+ void DeleteSlot(IndexSlot *slotPtr) noexcept;
+ void GarbageCollect() noexcept;
+ bool CheckIntegrity(const StringRef& errmsg) noexcept;
+ void Diagnostics(MessageType mt, Platform& p) noexcept;
+
+ extern ReadWriteLock heapLock;
+}
#endif /* SRC_PLATFORM_HEAP_H_ */
diff --git a/src/Platform/MessageBox.cpp b/src/Platform/MessageBox.cpp
new file mode 100644
index 00000000..f174b42d
--- /dev/null
+++ b/src/Platform/MessageBox.cpp
@@ -0,0 +1,140 @@
+/*
+ * MessageBox.cpp
+ *
+ * Created on: 26 Sept 2022
+ * Author: David
+ */
+
+#include "MessageBox.h"
+#include "RepRap.h"
+#include <GCodes/GCodeBuffer/GCodeBuffer.h>
+
+// Macro to build a standard lambda function that includes the necessary type conversions
+#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(MessageBox, __VA_ARGS__)
+#define OBJECT_MODEL_FUNC_IF(_condition,...) OBJECT_MODEL_FUNC_IF_BODY(MessageBox, _condition,__VA_ARGS__)
+
+constexpr ObjectModelTableEntry MessageBox::objectModelTable[] =
+{
+ { "axisControls", OBJECT_MODEL_FUNC_IF(self->mode == 2 || self->mode == 3, (int32_t)self->controls.GetRaw()), ObjectModelEntryFlags::important },
+ { "cancelButton", OBJECT_MODEL_FUNC(self->limits.canCancel), ObjectModelEntryFlags::important },
+ { "choices", OBJECT_MODEL_FUNC_IF(self->mode == 4, self->limits.choices), ObjectModelEntryFlags::important },
+ { "default", OBJECT_MODEL_FUNC_IF(self->mode >= 4, self->limits.defaultVal), ObjectModelEntryFlags::important },
+ { "max", OBJECT_MODEL_FUNC_IF(self->mode >= 5, self->limits.maxVal), ObjectModelEntryFlags::important },
+ { "message", OBJECT_MODEL_FUNC(self->message.c_str()), ObjectModelEntryFlags::important },
+ { "min", OBJECT_MODEL_FUNC_IF(self->mode >= 5, self->limits.minVal), ObjectModelEntryFlags::important },
+ { "mode", OBJECT_MODEL_FUNC((int32_t)self->mode), ObjectModelEntryFlags::important },
+ { "seq", OBJECT_MODEL_FUNC((int32_t)self->seq), ObjectModelEntryFlags::important },
+ { "timeout", OBJECT_MODEL_FUNC((int32_t)self->timeout), ObjectModelEntryFlags::important },
+ { "title", OBJECT_MODEL_FUNC(self->title.c_str()), ObjectModelEntryFlags::important },
+};
+
+constexpr uint8_t MessageBox::objectModelTableDescriptor[] =
+{
+ 1,
+ 11
+};
+
+DEFINE_GET_OBJECT_MODEL_TABLE(MessageBox)
+
+uint32_t MessageBox::nextSeq = 0;
+
+// Set a message box
+void MessageBox::Populate(const char *msg, const char *p_title, int p_mode, float p_timeout, AxesBitmap p_controls, MessageBoxLimits *_ecv_null p_limits) noexcept
+{
+ message.copy(msg);
+ title.copy(p_title);
+ mode = p_mode;
+ startTime = millis();
+ timeout = lrintf(max<float>(p_timeout, 0.0) * 1000.0);
+ controls = p_controls;
+ if (p_limits != nullptr)
+ {
+ limits = *p_limits;
+ }
+
+ // Override the canCancel flag if its value is implied by the mode
+ if (mode == 3)
+ {
+ limits.canCancel = true;
+ }
+ else if (mode < 3)
+ {
+ limits.canCancel = false;
+ }
+}
+
+float MessageBox::GetTimeLeft() const noexcept
+{
+ return (timeout != 0) ? ((float)timeout - (float)(millis() - startTime)) / 1000.0 : 0.0;
+}
+
+void MessageBoxLimits::GetIntegerLimits(GCodeBuffer& gb, bool defaultIsString) THROWS(GCodeException)
+{
+ int32_t iLow, iHigh;
+ bool dummy;
+ const bool gotLowerLimit = gb.TryGetIValue('L', iLow, dummy);
+ const bool gotUpperLimit = gb.TryGetIValue('H', iHigh, dummy);
+ if (gotLowerLimit)
+ {
+ if (gotUpperLimit && iLow > iHigh)
+ {
+ gb.ThrowGCodeException("H value must not be less than L value");
+ }
+ minVal.SetInt(iLow);
+ }
+ if (gotUpperLimit)
+ {
+ maxVal.SetInt(iHigh);
+ }
+ if (gb.Seen('F'))
+ {
+ int32_t iDefault;
+ if (defaultIsString)
+ {
+ String<StringLength100> sDefault;
+ gb.GetQuotedString(sDefault.GetRef(), true);
+ iDefault = (int32_t)sDefault.strlen();
+ defaultVal.SetStringHandle(StringHandle(sDefault.c_str()));
+ }
+ else
+ {
+ iDefault = gb.GetIValue();
+ defaultVal.SetInt(iDefault);
+ }
+ if ((gotLowerLimit && iDefault < iLow) || (gotUpperLimit && iDefault > iHigh))
+ {
+ gb.ThrowGCodeException("default value is outside limits");
+ }
+ }
+}
+
+void MessageBoxLimits::GetFloatLimits(GCodeBuffer& gb) THROWS(GCodeException)
+{
+ float fLow, fHigh, fDefault;
+ bool dummy;
+ const bool gotLowerLimit = gb.TryGetFValue('L', fLow, dummy);
+ const bool gotUpperLimit = gb.TryGetFValue('H', fHigh, dummy);
+ if (gotLowerLimit)
+ {
+ if (gotUpperLimit && fLow > fHigh)
+ {
+ gb.ThrowGCodeException("H value must not be less than L value");
+ }
+ minVal.SetFloat(fLow);
+ }
+ if (gotUpperLimit)
+ {
+ maxVal.SetFloat(fHigh);
+ }
+ const bool gotDefault = gb.TryGetFValue('F', fDefault, dummy);
+ if (gotDefault)
+ {
+ if ((gotLowerLimit && fDefault < fLow) || (gotUpperLimit && fDefault > fHigh))
+ {
+ gb.ThrowGCodeException("default value is outside limits");
+ }
+ defaultVal.SetFloat(fDefault);
+ }
+}
+
+// End
diff --git a/src/Platform/MessageBox.h b/src/Platform/MessageBox.h
new file mode 100644
index 00000000..298aafb4
--- /dev/null
+++ b/src/Platform/MessageBox.h
@@ -0,0 +1,77 @@
+/*
+ * MessageBox.h
+ *
+ * Created on: 26 Sept 2022
+ * Author: David
+ */
+
+#ifndef SRC_PLATFORM_MESSAGEBOX_H_
+#define SRC_PLATFORM_MESSAGEBOX_H_
+
+#include <RepRapFirmware.h>
+#include <ObjectModel/ObjectModel.h>
+#include <General/FreelistManager.h>
+
+// Struct to hold minimum, maximum and default values
+struct MessageBoxLimits
+{
+ MessageBoxLimits() noexcept { }
+ void GetIntegerLimits(GCodeBuffer& gb, bool defaultIsString) THROWS(GCodeException);
+ void GetFloatLimits(GCodeBuffer& gb) THROWS(GCodeException);
+
+ ExpressionValue minVal, maxVal, defaultVal, choices;
+ bool canCancel = false;
+};
+
+// Message box data
+class MessageBox INHERIT_OBJECT_MODEL
+{
+public:
+ DECLARE_FREELIST_NEW_DELETE(MessageBox)
+
+ MessageBox(MessageBox *p_next) noexcept : next(p_next) { seq = ++nextSeq; }
+
+ // Set a message box
+ void Populate(const char *msg, const char *p_title, int p_mode, float p_timeout, AxesBitmap p_controls, MessageBoxLimits *_ecv_null p_limits) noexcept;
+
+ // Return true if the mode of this message box is one that the legacy status calls can handle
+ bool IsLegacyType() const noexcept { return mode <= 3; } // legacy M407 and rr_status etc. don't handle higher message box modes
+
+ // Return true if this message box should be timed out
+ bool HasTimedOut() const noexcept { return timeout != 0 && millis() - startTime >= timeout; }
+
+ // Return the length of time before this box should time out
+ float GetTimeLeft() const noexcept;
+
+ // Return true if this message box blocks the input that created it
+ bool IsBlocking() const noexcept { return mode >= 2; }
+
+ MessageBox *GetNext() const noexcept { return next; }
+
+ // Return various data about the message box
+ AxesBitmap GetControls() const noexcept { return controls; }
+ int GetMode() const noexcept { return mode; }
+ uint32_t GetSeq() const noexcept { return seq; }
+ const char *GetMessage() const noexcept { return message.c_str(); }
+ const char *GetTitle() const noexcept { return title.c_str(); }
+ ExpressionValue GetDefaultValue() const noexcept { return limits.defaultVal; }
+ bool CanCancel() const noexcept { return limits.canCancel; }
+
+protected:
+ DECLARE_OBJECT_MODEL
+
+private:
+ MessageBox *next;
+ String<MaxMessageLength> message;
+ String<MaxTitleLength> title;
+ MessageBoxLimits limits;
+ int mode;
+ uint32_t seq;
+ uint32_t startTime;
+ uint32_t timeout;
+ AxesBitmap controls;
+
+ static uint32_t nextSeq;
+};
+
+#endif /* SRC_PLATFORM_MESSAGEBOX_H_ */
diff --git a/src/Platform/Platform.cpp b/src/Platform/Platform.cpp
index bb7b2135..7e003bf6 100644
--- a/src/Platform/Platform.cpp
+++ b/src/Platform/Platform.cpp
@@ -11,7 +11,7 @@
18 November 2012
- Adrian Bowyer
+ Adrian Bowyerinfo[1]
RepRap Professional Ltd
http://reprappro.com
@@ -38,7 +38,7 @@
#include "Logger.h"
#include "Tasks.h"
#include <Cache.h>
-#include <Hardware/SharedSpi/SharedSpiDevice.h>
+#include <Hardware/Spi/SharedSpiDevice.h>
#include <Math/Isqrt.h>
#include <Hardware/I2C.h>
#include <Hardware/NonVolatileMemory.h>
@@ -77,7 +77,7 @@ using AnalogIn::AdcBits; // for compatibility with CoreNG, which doesn't have
# include <Comms/FirmwareUpdater.h>
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
# include "Display/Display.h"
#endif
@@ -204,21 +204,25 @@ DriversBitmap AxisDriversConfig::GetDriversBitmap() const noexcept
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Platform, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(...) OBJECT_MODEL_FUNC_IF_BODY(Platform, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor Platform::axisDriversArrayDescriptor =
+constexpr ObjectModelArrayTableEntry Platform::objectModelArrayTable[] =
{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((const Platform*)self)->axisDrivers[context.GetLastIndex()].numDrivers; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const Platform*)self)->axisDrivers[context.GetIndex(1)].driverNumbers[context.GetLastIndex()]); }
+ // 0. Axis drivers
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return ((const Platform*)self)->axisDrivers[context.GetLastIndex()].numDrivers; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const Platform*)self)->axisDrivers[context.GetIndex(1)].driverNumbers[context.GetLastIndex()]); }
+ },
+ // 1. Workplace coordinate offsets
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return NumCoordinateSystems; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(reprap.GetGCodes().GetWorkplaceOffset(context.GetIndex(1), context.GetLastIndex()), 3); }
+ }
};
-constexpr ObjectModelArrayDescriptor Platform::workplaceOffsetsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return NumCoordinateSystems; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(reprap.GetGCodes().GetWorkplaceOffset(context.GetIndex(1), context.GetIndex(0)), 3); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(Platform)
static inline const char *_ecv_array GetFilamentName(size_t extruder) noexcept
{
@@ -235,7 +239,7 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] =
#if SUPPORT_CAN_EXPANSION
{ "canAddress", OBJECT_MODEL_FUNC_NOSELF((int32_t)CanInterface::GetCanAddress()), ObjectModelEntryFlags::none },
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
{ "directDisplay", OBJECT_MODEL_FUNC_IF_NOSELF(reprap.GetDisplay().IsPresent(), &reprap.GetDisplay()), ObjectModelEntryFlags::none },
#endif
{ "firmwareDate", OBJECT_MODEL_FUNC_NOSELF(DATE), ObjectModelEntryFlags::none },
@@ -260,7 +264,7 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] =
{ "name", OBJECT_MODEL_FUNC_NOSELF(BOARD_NAME), ObjectModelEntryFlags::none },
{ "shortName", OBJECT_MODEL_FUNC_NOSELF(BOARD_SHORT_NAME), ObjectModelEntryFlags::none },
#endif
- { "supportsDirectDisplay", OBJECT_MODEL_FUNC_NOSELF(SUPPORT_12864_LCD ? true : false), ObjectModelEntryFlags::verbose },
+ { "supportsDirectDisplay", OBJECT_MODEL_FUNC_NOSELF(SUPPORT_DIRECT_LCD ? true : false), ObjectModelEntryFlags::verbose },
#if MCU_HAS_UNIQUE_ID
{ "uniqueId", OBJECT_MODEL_FUNC_IF(self->uniqueId.IsValid(), self->uniqueId), ObjectModelEntryFlags::none },
#endif
@@ -291,11 +295,11 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] =
{ "acceleration", OBJECT_MODEL_FUNC(InverseConvertAcceleration(self->Acceleration(context.GetLastIndex())), 1), ObjectModelEntryFlags::none },
{ "babystep", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetTotalBabyStepOffset(context.GetLastIndex()), 3), ObjectModelEntryFlags::none },
{ "current", OBJECT_MODEL_FUNC((int32_t)(self->GetMotorCurrent(context.GetLastIndex(), 906))), ObjectModelEntryFlags::none },
- { "drivers", OBJECT_MODEL_FUNC_NOSELF(&axisDriversArrayDescriptor), ObjectModelEntryFlags::none },
+ { "drivers", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
{ "homed", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().IsAxisHomed(context.GetLastIndex())), ObjectModelEntryFlags::none },
{ "jerk", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerMin(self->GetInstantDv(context.GetLastIndex())), 1), ObjectModelEntryFlags::none },
{ "letter", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetAxisLetters()[context.GetLastIndex()]), ObjectModelEntryFlags::none },
- { "machinePosition", OBJECT_MODEL_FUNC_NOSELF(reprap.GetMove().LiveCoordinate(context.GetLastIndex(), reprap.GetCurrentTool()), 3), ObjectModelEntryFlags::live },
+ { "machinePosition", OBJECT_MODEL_FUNC_NOSELF(reprap.GetMove().LiveCoordinate(context.GetLastIndex(), reprap.GetGCodes().GetCurrentMovementState(context).currentTool), 3), ObjectModelEntryFlags::live },
{ "max", OBJECT_MODEL_FUNC(self->AxisMaximum(context.GetLastIndex()), 2), ObjectModelEntryFlags::none },
{ "maxProbed", OBJECT_MODEL_FUNC(self->axisMaximaProbed.IsBitSet(context.GetLastIndex())), ObjectModelEntryFlags::none },
{ "microstepping", OBJECT_MODEL_FUNC(self, 7), ObjectModelEntryFlags::none },
@@ -307,9 +311,9 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] =
#endif
{ "speed", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerMin(self->MaxFeedrate(context.GetLastIndex())), 1), ObjectModelEntryFlags::none },
{ "stepsPerMm", OBJECT_MODEL_FUNC(self->driveStepsPerUnit[context.GetLastIndex()], 2), ObjectModelEntryFlags::none },
- { "userPosition", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetUserCoordinate(context.GetLastIndex()), 3), ObjectModelEntryFlags::live },
+ { "userPosition", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetUserCoordinate(reprap.GetGCodes().GetCurrentMovementState(context), context.GetLastIndex()), 3), ObjectModelEntryFlags::live },
{ "visible", OBJECT_MODEL_FUNC_NOSELF(context.GetLastIndex() < (int32_t)reprap.GetGCodes().GetVisibleAxes()), ObjectModelEntryFlags::none },
- { "workplaceOffsets", OBJECT_MODEL_FUNC_NOSELF(&workplaceOffsetsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "workplaceOffsets", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
// 4. move.extruders[] members
{ "acceleration", OBJECT_MODEL_FUNC(InverseConvertAcceleration(self->Acceleration(ExtruderToLogicalDrive(context.GetLastIndex()))), 1), ObjectModelEntryFlags::none },
@@ -317,6 +321,7 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] =
{ "driver", OBJECT_MODEL_FUNC(self->extruderDrivers[context.GetLastIndex()]), ObjectModelEntryFlags::none },
{ "factor", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetExtrusionFactor(context.GetLastIndex()), 3), ObjectModelEntryFlags::none },
{ "filament", OBJECT_MODEL_FUNC_NOSELF(GetFilamentName(context.GetLastIndex())), ObjectModelEntryFlags::none },
+ { "filamentDiameter", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetFilamentDiameter(context.GetLastIndex()), 3), ObjectModelEntryFlags::none },
{ "jerk", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerMin(self->GetInstantDv(ExtruderToLogicalDrive(context.GetLastIndex()))), 1), ObjectModelEntryFlags::none },
{ "microstepping", OBJECT_MODEL_FUNC(self, 8), ObjectModelEntryFlags::none },
{ "nonlinear", OBJECT_MODEL_FUNC(self, 5), ObjectModelEntryFlags::none },
@@ -324,7 +329,7 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] =
#ifndef DUET_NG
{ "percentStstCurrent", OBJECT_MODEL_FUNC((int32_t)(self->GetMotorCurrent(context.GetLastIndex(), 917))), ObjectModelEntryFlags::none },
#endif
- { "position", OBJECT_MODEL_FUNC_NOSELF(ExpressionValue(reprap.GetMove().LiveCoordinate(ExtruderToLogicalDrive(context.GetLastIndex()), reprap.GetCurrentTool()), 1)), ObjectModelEntryFlags::live },
+ { "position", OBJECT_MODEL_FUNC_NOSELF(ExpressionValue(reprap.GetMove().LiveCoordinate(ExtruderToLogicalDrive(context.GetLastIndex()), reprap.GetGCodes().GetCurrentMovementState(context).currentTool), 1)), ObjectModelEntryFlags::live },
{ "pressureAdvance", OBJECT_MODEL_FUNC_NOSELF(reprap.GetMove().GetPressureAdvanceClocks(context.GetLastIndex())/StepClockRate, 2), ObjectModelEntryFlags::none },
{ "rawPosition", OBJECT_MODEL_FUNC_NOSELF(ExpressionValue(reprap.GetGCodes().GetRawExtruderTotalByDrive(context.GetLastIndex()), 1)), ObjectModelEntryFlags::live },
{ "speed", OBJECT_MODEL_FUNC(InverseConvertSpeedToMmPerMin(self->MaxFeedrate(ExtruderToLogicalDrive(context.GetLastIndex()))), 1), ObjectModelEntryFlags::none },
@@ -361,7 +366,7 @@ constexpr uint8_t Platform::objectModelTableDescriptor[] =
{
10, // number of sections
9 + SUPPORT_ACCELEROMETERS + HAS_SBC_INTERFACE + HAS_MASS_STORAGE + HAS_VOLTAGE_MONITOR + HAS_12V_MONITOR + HAS_CPU_TEMP_SENSOR
- + SUPPORT_CAN_EXPANSION + SUPPORT_12864_LCD + MCU_HAS_UNIQUE_ID + HAS_WIFI_NETWORKING, // section 0: boards[0]
+ + SUPPORT_CAN_EXPANSION + SUPPORT_DIRECT_LCD + MCU_HAS_UNIQUE_ID + HAS_WIFI_NETWORKING, // section 0: boards[0]
#if HAS_CPU_TEMP_SENSOR
3, // section 1: mcuTemp
#else
@@ -374,10 +379,10 @@ constexpr uint8_t Platform::objectModelTableDescriptor[] =
#endif
#ifdef DUET_NG // Duet WiFi/Ethernet doesn't have settable standstill current
19, // section 3: move.axes[]
- 14, // section 4: move.extruders[]
+ 15, // section 4: move.extruders[]
#else
20, // section 3: move.axes[]
- 15, // section 4: move.extruders[]
+ 16, // section 4: move.extruders[]
#endif
3, // section 5: move.extruders[].nonlinear
#if HAS_12V_MONITOR
@@ -631,6 +636,13 @@ void Platform::Init() noexcept
}
// Set up the local drivers. Do this after we have read any direction pins that specify the board type.
+#if defined(DUET3MINI) && SUPPORT_TMC2240
+ // Check whether we have a TMC2240 prototype expansion board connected, before we set the driver direction pins to outputs
+ pinMode(DIRECTION_PINS[5], INPUT_PULLUP);
+ delayMicroseconds(20); // give the pullup resistor time to work
+ hasTmc2240Expansion = !digitalRead(DIRECTION_PINS[5]);
+#endif
+
#ifdef DUET3_MB6XD
ENABLE_PINS = (GetBoardType() == BoardType::Duet3_6XD_v01) ? ENABLE_PINS_v01 : ENABLE_PINS_v100;
unsigned int numErrorHighDrivers = 0;
@@ -734,6 +746,8 @@ void Platform::Init() noexcept
# elif SUPPORT_TMC22xx
# if TMC22xx_VARIABLE_NUM_DRIVERS
SmartDrivers::Init(numSmartDrivers);
+# elif SUPPORT_TMC2240 && defined(DUET3MINI)
+ SmartDrivers::Init(hasTmc2240Expansion);
# else
SmartDrivers::Init();
# endif
@@ -1204,7 +1218,7 @@ void Platform::Spin() noexcept
}
else if (logOnStallDrivers.Intersects(mask))
{
- MessageF(WarningMessage, "Driver %u stalled at Z height %.2f\n", nextDriveToPoll, (double)reprap.GetMove().LiveCoordinate(Z_AXIS, reprap.GetCurrentTool()));
+ MessageF(WarningMessage, "Driver %u stalled at Z height %.2f\n", nextDriveToPoll, (double)reprap.GetMove().LiveCoordinate(Z_AXIS, reprap.GetGCodes().GetPrimaryMovementState().currentTool));
}
}
}
@@ -1739,7 +1753,7 @@ void Platform::Diagnostics(MessageType mtype) noexcept
ResetVoltageMonitors();
- StringHandle::Diagnostics(mtype, *this);
+ Heap::Diagnostics(mtype, *this);
Event::Diagnostics(mtype, *this);
// Show the motor position and stall status
@@ -2226,16 +2240,13 @@ GCodeResult Platform::DiagnosticTest(GCodeBuffer& gb, const StringRef& reply, Ou
MessageF(MessageType::GenericMessage,
"\nPrintMonitor %08" PRIx32 "-%08" PRIx32
"\nFansManager %08" PRIx32 "-%08" PRIx32
-#if SUPPORT_ROLAND
- "\nRoland %08" PRIx32 "-%08" PRIx32
-#endif
#if SUPPORT_SCANNER
"\nScanner %08" PRIx32 "-%08" PRIx32
#endif
#if SUPPORT_IOBITS
"\nPortControl %08" PRIx32 "-%08" PRIx32
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
"\nDisplay %08" PRIx32 "-%08" PRIx32
#endif
#if SUPPORT_CAN_EXPANSION
@@ -2244,16 +2255,13 @@ GCodeResult Platform::DiagnosticTest(GCodeBuffer& gb, const StringRef& reply, Ou
, reinterpret_cast<uint32_t>(&reprap.GetPrintMonitor()), reinterpret_cast<uint32_t>(&reprap.GetPrintMonitor()) + sizeof(PrintMonitor) - 1
, reinterpret_cast<uint32_t>(&reprap.GetFansManager()), reinterpret_cast<uint32_t>(&reprap.GetFansManager()) + sizeof(FansManager) - 1
-#if SUPPORT_ROLAND
- , reinterpret_cast<uint32_t>(&reprap.GetRoland()), reinterpret_cast<uint32_t>(&reprap.GetRoland()) + sizeof(Roland) - 1
-#endif
#if SUPPORT_SCANNER
, reinterpret_cast<uint32_t>(&reprap.GetScanner()), reinterpret_cast<uint32_t>(&reprap.GetScanner()) + sizeof(Scanner) - 1
#endif
#if SUPPORT_IOBITS
, reinterpret_cast<uint32_t>(&reprap.GetPortControl()), reinterpret_cast<uint32_t>(&reprap.GetPortControl()) + sizeof(PortControl) - 1
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
, reinterpret_cast<uint32_t>(&reprap.GetDisplay()), reinterpret_cast<uint32_t>(&reprap.GetDisplay()) + sizeof(Display) - 1
#endif
#if SUPPORT_CAN_EXPANSION
@@ -3517,39 +3525,6 @@ void Platform::DebugMessage(const char *_ecv_array fmt, va_list vargs) noexcept
);
}
-// Send a message box, which may require an acknowledgement
-// sParam = 0 Just display the message box, optional timeout
-// sParam = 1 As for 0 but display a Close button as well
-// sParam = 2 Display the message box with an OK button, wait for acknowledgement (waiting is set up by the caller)
-// sParam = 3 As for 2 but also display a Cancel button
-void Platform::SendAlert(MessageType mt, const char *_ecv_array message, const char *_ecv_array title, int sParam, float tParam, AxesBitmap controls) noexcept
-{
- if ((mt & (HttpMessage | AuxMessage | LcdMessage | BinaryCodeReplyFlag)) != 0)
- {
- reprap.SetAlert(message, title, sParam, tParam, controls); // make the RepRap class cache this message until it's picked up by the HTTP clients and/or PanelDue
- }
-
- MessageF(MessageType::LogInfo, "M291: - %s - %s", (strlen(title) > 0 ? title : "[no title]"), message);
-
- mt = (MessageType)(mt & (UsbMessage | TelnetMessage));
- if (mt != 0)
- {
- if (strlen(title) > 0)
- {
- MessageF(mt, "- %s -\n", title);
- }
- MessageF(mt, "%s\n", message);
- if (sParam == 2)
- {
- Message(mt, "Send M292 to continue\n");
- }
- else if (sParam == 3)
- {
- Message(mt, "Send M292 to continue or M292 P1 to cancel\n");
- }
- }
-}
-
#if HAS_MASS_STORAGE
// Configure logging according to the M929 command received, returning true if error
@@ -4069,8 +4044,7 @@ void Platform::AppendSysDir(const StringRef & path) const noexcept
ReadLockedPointer<const char> Platform::GetSysDir() const noexcept
{
- ReadLocker lock(sysDirLock);
- return ReadLockedPointer<const char>(lock, InternalGetSysDir());
+ return ReadLockedPointer<const char>(sysDirLock, InternalGetSysDir());
}
#endif
@@ -4585,7 +4559,7 @@ GCodeResult Platform::ConfigurePort(GCodeBuffer& gb, const StringRef& reply) THR
# if HAS_SBC_INTERFACE
if (reprap.UsingSbcInterface())
{
- reply.copy("SD card not supported in SBC mode");
+ reply.copy("SD card attached to Duet is not supported in SBC mode");
return GCodeResult::error;
}
# endif
diff --git a/src/Platform/Platform.h b/src/Platform/Platform.h
index 22735a98..f33c633a 100644
--- a/src/Platform/Platform.h
+++ b/src/Platform/Platform.h
@@ -435,7 +435,6 @@ public:
void MessageV(MessageType type, const char *_ecv_array fmt, va_list vargs) noexcept;
void DebugMessage(const char *_ecv_array fmt, va_list vargs) noexcept;
bool FlushMessages() noexcept; // Flush messages to USB and aux, returning true if there is more to send
- void SendAlert(MessageType mt, const char *_ecv_array message, const char *_ecv_array title, int sParam, float tParam, AxesBitmap controls) noexcept;
void StopLogging() noexcept;
// Movement
@@ -685,10 +684,12 @@ public:
void InvertDiagLed() const noexcept;
#endif
+#if defined(DUET3MINI) && SUPPORT_TMC2240
+ const char *_ecv_array null GetExpansionBoardName() const noexcept { return (hasTmc2240Expansion) ? "Duet3 Mini 2+ (TMC2240)" : nullptr; }
+#endif
+
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(axisDrivers)
- OBJECT_MODEL_ARRAY(workplaceOffsets)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
const char *_ecv_array InternalGetSysDir() const noexcept; // where the system files are - not thread-safe!
@@ -749,7 +750,7 @@ private:
void SetDriverDirection(uint8_t driver, bool direction) noexcept
pre(driver < NumDirectDrivers);
-#if VARIABLE_NUM_DRIVERS && SUPPORT_12864_LCD
+#if VARIABLE_NUM_DRIVERS && SUPPORT_DIRECT_LCD
size_t numActualDirectDrivers;
#endif
@@ -817,6 +818,10 @@ private:
bool warnDriversNotPowered;
#endif
+#if defined(DUET3MINI) && SUPPORT_TMC2240
+ bool hasTmc2240Expansion;
+#endif
+
#if HAS_STALL_DETECT
DriversBitmap logOnStallDrivers, eventOnStallDrivers;
#endif
diff --git a/src/Platform/RepRap.cpp b/src/Platform/RepRap.cpp
index 0353012c..58df6a70 100644
--- a/src/Platform/RepRap.cpp
+++ b/src/Platform/RepRap.cpp
@@ -42,7 +42,7 @@
# include "PortControl.h"
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
# include "Display/Display.h"
#endif
@@ -160,102 +160,102 @@ extern "C" void hsmciIdle(uint32_t stBits, uint32_t dmaBits) noexcept
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(RepRap, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(_condition,...) OBJECT_MODEL_FUNC_IF_BODY(RepRap, _condition,__VA_ARGS__)
-constexpr ObjectModelArrayDescriptor RepRap::boardsArrayDescriptor =
+constexpr ObjectModelArrayTableEntry RepRap::objectModelArrayTable[] =
{
- nullptr, // no lock needed
+ // 0. Boards
+ {
+ nullptr, // no lock needed
#if SUPPORT_CAN_EXPANSION
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->expansion->GetNumExpansionBoards() + 1; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return (context.GetLastIndex() == 0)
- ? ExpressionValue(((const RepRap*)self)->platform, 0)
- : ExpressionValue(((const RepRap*)self)->expansion, 0); }
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->expansion->GetNumExpansionBoards() + 1; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return (context.GetLastIndex() == 0)
+ ? ExpressionValue(((const RepRap*)self)->platform, 0)
+ : ExpressionValue(((const RepRap*)self)->expansion, 0); }
#else
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 1; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->platform, 0); }
-#endif
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::fansArrayDescriptor =
-{
- &FansManager::fansLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->fansManager->GetNumFansToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->fansManager->FindFan(context.GetLastIndex()).Ptr()); }
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::inputsArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->gCodes->GetNumInputs(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->gCodes->GetInput(context.GetLastIndex())); }
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::gpoutArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetPlatform().GetNumGpOutputsToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- {
- const GpOutputPort& port = reprap.GetPlatform().GetGpOutPort(context.GetLastIndex());
- return (port.IsUnused()) ? ExpressionValue(nullptr) : ExpressionValue(&port);
- }
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::spindlesArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return MaxSpindles; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&((const RepRap*)self)->platform->AccessSpindle(context.GetLastIndex())); }
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::toolsArrayDescriptor =
-{
- &toolListLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->numToolsToReport; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->GetTool(context.GetLastIndex()).Ptr()); }
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::restorePointsArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return NumRestorePoints; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const RepRap*)self)->gCodes->GetRestorePoint(context.GetLastIndex())); }
-};
-
-constexpr ObjectModelArrayDescriptor RepRap::volumesArrayDescriptor =
-{
- nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 1; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->platform, 0); }
+#endif
+ },
+ // 1. Fans
+ {
+ &FansManager::fansLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->fansManager->GetNumFansToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->fansManager->FindFan(context.GetLastIndex()).Ptr()); }
+ },
+ // 2. Inputs
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const RepRap*)self)->gCodes->GetNumInputs(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const RepRap*)self)->gCodes->GetInput(context.GetLastIndex())); }
+ },
+ // 3. Spindles
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return MaxSpindles; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&((const RepRap*)self)->platform->AccessSpindle(context.GetLastIndex())); }
+ },
+ // 4. Tools
+ {
+ &Tool::toolListLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return Tool::GetNumToolsToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(Tool::GetLockedTool(context.GetLastIndex()).Ptr()); }
+ },
+ // 5. Volumes
+ {
+ nullptr,
#if HAS_MASS_STORAGE
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MassStorage::GetNumVolumes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(MassStorage::GetVolume(context.GetLastIndex())); }
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MassStorage::GetNumVolumes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(MassStorage::GetVolume(context.GetLastIndex())); }
#else
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 0; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(nullptr); }
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 0; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(nullptr); }
#endif
-};
+ },
+ // 6. GP outputs
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetPlatform().GetNumGpOutputsToReport(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ {
+ const GpOutputPort& port = reprap.GetPlatform().GetGpOutPort(context.GetLastIndex());
+ return (port.IsUnused()) ? ExpressionValue(nullptr) : ExpressionValue(&port);
+ }
+ },
+ // 7. Restore points
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return NumVisibleRestorePoints; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(&((const RepRap*)self)->gCodes->GetCurrentMovementState(context).restorePoints[context.GetLastIndex()]); }
+ }
#if HAS_MASS_STORAGE
-constexpr ObjectModelArrayDescriptor RepRap::volChangesArrayDescriptor =
-{
- nullptr,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MassStorage::GetNumVolumes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue((int32_t)MassStorage::GetVolumeSeq(context.GetLastIndex())); }
-};
+ ,
+ // 8. Volume changes
+ {
+ nullptr,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return MassStorage::GetNumVolumes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue((int32_t)MassStorage::GetVolumeSeq(context.GetLastIndex())); }
+ }
#endif
+};
+
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(RepRap)
+constexpr unsigned int StateSubTableNumber = 3; // section number of 'state' in the following
constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. MachineModel root
- { "boards", OBJECT_MODEL_FUNC_NOSELF(&boardsArrayDescriptor), ObjectModelEntryFlags::live },
+ { "boards", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::live },
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
{ "directories", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none },
#endif
- { "fans", OBJECT_MODEL_FUNC_NOSELF(&fansArrayDescriptor), ObjectModelEntryFlags::live },
+ { "fans", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::live },
{ "global", OBJECT_MODEL_FUNC(&(self->globalVariables)), ObjectModelEntryFlags::none },
{ "heat", OBJECT_MODEL_FUNC(self->heat), ObjectModelEntryFlags::live },
- { "inputs", OBJECT_MODEL_FUNC_NOSELF(&inputsArrayDescriptor), ObjectModelEntryFlags::live },
+ { "inputs", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::live },
{ "job", OBJECT_MODEL_FUNC(self->printMonitor), ObjectModelEntryFlags::live },
{ "limits", OBJECT_MODEL_FUNC(self, 2), ObjectModelEntryFlags::none },
{ "move", OBJECT_MODEL_FUNC(self->move), ObjectModelEntryFlags::live },
@@ -265,11 +265,11 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{ "scanner", OBJECT_MODEL_FUNC(self->scanner), ObjectModelEntryFlags::none },
#endif
{ "sensors", OBJECT_MODEL_FUNC(&self->platform->GetEndstops()), ObjectModelEntryFlags::live },
- { "seqs", OBJECT_MODEL_FUNC(self, 6), ObjectModelEntryFlags::live },
- { "spindles", OBJECT_MODEL_FUNC_NOSELF(&spindlesArrayDescriptor), ObjectModelEntryFlags::live },
+ { "seqs", OBJECT_MODEL_FUNC(self, 5), ObjectModelEntryFlags::live },
+ { "spindles", OBJECT_MODEL_FUNC_ARRAY(3), ObjectModelEntryFlags::live },
{ "state", OBJECT_MODEL_FUNC(self, 3), ObjectModelEntryFlags::live },
- { "tools", OBJECT_MODEL_FUNC_NOSELF(&toolsArrayDescriptor), ObjectModelEntryFlags::live },
- { "volumes", OBJECT_MODEL_FUNC_NOSELF(&volumesArrayDescriptor), ObjectModelEntryFlags::none },
+ { "tools", OBJECT_MODEL_FUNC_ARRAY(4), ObjectModelEntryFlags::live },
+ { "volumes", OBJECT_MODEL_FUNC_ARRAY(5), ObjectModelEntryFlags::none },
// 1. MachineModel.directories
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
@@ -307,7 +307,7 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{ "heaters", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxHeaters), ObjectModelEntryFlags::verbose },
{ "heatersPerTool", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxHeatersPerTool), ObjectModelEntryFlags::verbose },
{ "monitorsPerHeater", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxMonitorsPerHeater), ObjectModelEntryFlags::verbose },
- { "restorePoints", OBJECT_MODEL_FUNC_NOSELF((int32_t)NumRestorePoints), ObjectModelEntryFlags::verbose },
+ { "restorePoints", OBJECT_MODEL_FUNC_NOSELF((int32_t)NumVisibleRestorePoints), ObjectModelEntryFlags::verbose },
{ "sensors", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxSensors), ObjectModelEntryFlags::verbose },
{ "spindles", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxSpindles), ObjectModelEntryFlags::verbose },
{ "tools", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxTools), ObjectModelEntryFlags::verbose },
@@ -322,14 +322,14 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{ "zProbeProgramBytes", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxZProbeProgramBytes), ObjectModelEntryFlags::verbose },
{ "zProbes", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxZProbes), ObjectModelEntryFlags::verbose },
- // 3. MachineModel.state
+ // 3. MachineModel.state (see declaration of StateSubTableNumber above)
{ "atxPower", OBJECT_MODEL_FUNC_IF(self->platform->IsAtxPowerControlled(), self->platform->GetAtxPowerState()), ObjectModelEntryFlags::none },
{ "atxPowerPort", OBJECT_MODEL_FUNC_IF(self->platform->IsAtxPowerControlled(), self->platform->GetAtxPowerPort()), ObjectModelEntryFlags::none },
{ "beep", OBJECT_MODEL_FUNC_IF(self->beepDuration != 0, self, 4), ObjectModelEntryFlags::none },
- { "currentTool", OBJECT_MODEL_FUNC((int32_t)self->GetCurrentToolNumber()), ObjectModelEntryFlags::live },
+ { "currentTool", OBJECT_MODEL_FUNC((int32_t)self->gCodes->GetCurrentMovementState(context).GetCurrentToolNumber()), ObjectModelEntryFlags::live },
{ "deferredPowerDown", OBJECT_MODEL_FUNC_IF(self->platform->IsAtxPowerControlled(), self->platform->IsDeferredPowerDown()), ObjectModelEntryFlags::none },
{ "displayMessage", OBJECT_MODEL_FUNC(self->message.c_str()), ObjectModelEntryFlags::none },
- { "gpOut", OBJECT_MODEL_FUNC_NOSELF(&gpoutArrayDescriptor), ObjectModelEntryFlags::live },
+ { "gpOut", OBJECT_MODEL_FUNC_ARRAY(6), ObjectModelEntryFlags::live },
#if SUPPORT_LASER
// 2020-04-24: return the configured laser PWM even if the laser is temporarily turned off
{ "laserPwm", OBJECT_MODEL_FUNC_IF(self->gCodes->GetMachineType() == MachineType::laser, self->gCodes->GetLaserPwm(), 2), ObjectModelEntryFlags::live },
@@ -342,14 +342,14 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{ "logLevel", OBJECT_MODEL_FUNC(self->platform->GetLogLevel()), ObjectModelEntryFlags::none },
{ "machineMode", OBJECT_MODEL_FUNC(self->gCodes->GetMachineModeString()), ObjectModelEntryFlags::none },
{ "macroRestarted", OBJECT_MODEL_FUNC(self->gCodes->GetMacroRestarted()), ObjectModelEntryFlags::none },
- { "messageBox", OBJECT_MODEL_FUNC_IF(self->mbox.active, self, 5), ObjectModelEntryFlags::important },
+ { "messageBox", OBJECT_MODEL_FUNC_IF(self->mboxList != nullptr, self->mboxList, 0), ObjectModelEntryFlags::important },
{ "msUpTime", OBJECT_MODEL_FUNC_NOSELF((int32_t)(context.GetStartMillis() % 1000u)), ObjectModelEntryFlags::live },
- { "nextTool", OBJECT_MODEL_FUNC((int32_t)self->gCodes->GetNewToolNumber()), ObjectModelEntryFlags::none },
+ { "nextTool", OBJECT_MODEL_FUNC((int32_t)self->gCodes->GetCurrentMovementState(context).newToolNumber), ObjectModelEntryFlags::none },
#if HAS_VOLTAGE_MONITOR
{ "powerFailScript", OBJECT_MODEL_FUNC(self->gCodes->GetPowerFailScript()), ObjectModelEntryFlags::none },
#endif
- { "previousTool", OBJECT_MODEL_FUNC((int32_t)self->previousToolNumber), ObjectModelEntryFlags::none },
- { "restorePoints", OBJECT_MODEL_FUNC_NOSELF(&restorePointsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "previousTool", OBJECT_MODEL_FUNC((int32_t)self->gCodes->GetCurrentMovementState(context).previousToolNumber), ObjectModelEntryFlags::none },
+ { "restorePoints", OBJECT_MODEL_FUNC_ARRAY(7), ObjectModelEntryFlags::none },
{ "status", OBJECT_MODEL_FUNC(self->GetStatusString()), ObjectModelEntryFlags::live },
{ "thisInput", OBJECT_MODEL_FUNC_IF_NOSELF(context.GetGCodeBuffer() != nullptr, (int32_t)context.GetGCodeBuffer()->GetChannel().ToBaseType()), ObjectModelEntryFlags::verbose },
{ "time", OBJECT_MODEL_FUNC(DateTime(self->platform->GetDateTime())), ObjectModelEntryFlags::live },
@@ -359,15 +359,7 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{ "duration", OBJECT_MODEL_FUNC((int32_t)self->beepDuration), ObjectModelEntryFlags::none },
{ "frequency", OBJECT_MODEL_FUNC((int32_t)self->beepFrequency), ObjectModelEntryFlags::none },
- // 5. MachineModel.state.messageBox (FIXME acquire MutexLocker when accessing the following)
- { "axisControls", OBJECT_MODEL_FUNC((int32_t)self->mbox.controls.GetRaw()), ObjectModelEntryFlags::important },
- { "message", OBJECT_MODEL_FUNC(self->mbox.message.c_str()), ObjectModelEntryFlags::important },
- { "mode", OBJECT_MODEL_FUNC((int32_t)self->mbox.mode), ObjectModelEntryFlags::important },
- { "seq", OBJECT_MODEL_FUNC((int32_t)self->mbox.seq), ObjectModelEntryFlags::important },
- { "timeout", OBJECT_MODEL_FUNC((int32_t)self->mbox.timeout), ObjectModelEntryFlags::important },
- { "title", OBJECT_MODEL_FUNC(self->mbox.title.c_str()), ObjectModelEntryFlags::important },
-
- // 6. MachineModel.seqs
+ // 5. MachineModel.seqs
{ "boards", OBJECT_MODEL_FUNC((int32_t)self->boardsSeq), ObjectModelEntryFlags::live },
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
{ "directories", OBJECT_MODEL_FUNC((int32_t)self->directoriesSeq), ObjectModelEntryFlags::live },
@@ -392,14 +384,19 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] =
{ "state", OBJECT_MODEL_FUNC((int32_t)self->stateSeq), ObjectModelEntryFlags::live },
{ "tools", OBJECT_MODEL_FUNC((int32_t)self->toolsSeq), ObjectModelEntryFlags::live },
#if HAS_MASS_STORAGE
- { "volChanges", OBJECT_MODEL_FUNC_NOSELF(&volChangesArrayDescriptor), ObjectModelEntryFlags::live },
+ { "volChanges", OBJECT_MODEL_FUNC_ARRAY(8), ObjectModelEntryFlags::live },
{ "volumes", OBJECT_MODEL_FUNC((int32_t)self->volumesSeq), ObjectModelEntryFlags::live },
#endif
};
+ReadWriteLock *_ecv_null RepRap::GetObjectLock(unsigned int tableNumber) const noexcept /*override*/
+{
+ return (tableNumber == StateSubTableNumber) ? &mboxLock : nullptr;
+}
+
constexpr uint8_t RepRap::objectModelTableDescriptor[] =
{
- 7, // number of sub-tables
+ 6, // number of sub-tables
15 + SUPPORT_SCANNER + (HAS_MASS_STORAGE | HAS_EMBEDDED_FILES | HAS_SBC_INTERFACE), // root
#if HAS_MASS_STORAGE || HAS_EMBEDDED_FILES || HAS_SBC_INTERFACE
8, // directories
@@ -409,7 +406,6 @@ constexpr uint8_t RepRap::objectModelTableDescriptor[] =
25, // limits
20 + HAS_VOLTAGE_MONITOR + SUPPORT_LASER, // state
2, // state.beep
- 6, // state.messageBox
12 + HAS_NETWORKING + SUPPORT_SCANNER +
2 * HAS_MASS_STORAGE + (HAS_MASS_STORAGE | HAS_EMBEDDED_FILES | HAS_SBC_INTERFACE) // seqs
};
@@ -418,8 +414,6 @@ DEFINE_GET_OBJECT_MODEL_TABLE(RepRap)
#endif
-ReadWriteLock RepRap::toolListLock;
-
// RepRap member functions.
// Do nothing more in the constructor; put what you want in RepRap:Init()
@@ -427,11 +421,9 @@ ReadWriteLock RepRap::toolListLock;
RepRap::RepRap() noexcept
: boardsSeq(0), directoriesSeq(0), fansSeq(0), heatSeq(0), inputsSeq(0), jobSeq(0), moveSeq(0), globalSeq(0),
networkSeq(0), scannerSeq(0), sensorsSeq(0), spindlesSeq(0), stateSeq(0), toolsSeq(0), volumesSeq(0),
- toolList(nullptr), currentTool(nullptr), lastWarningMillis(0),
- activeExtruders(0), activeToolHeaters(0), numToolsToReport(0),
+ lastWarningMillis(0),
ticksInSpinState(0), heatTaskIdleTicks(0),
beepFrequency(0), beepDuration(0), beepTimer(0),
- previousToolNumber(-1),
diagnosticsDestination(MessageType::NoDestinationMessage), justSentDiagnostics(false),
spinningModule(noModule), stopped(false), active(false), processingConfig(true)
#if HAS_SBC_INTERFACE
@@ -485,16 +477,13 @@ void RepRap::Init() noexcept
printMonitor = new PrintMonitor(*platform, *gCodes);
fansManager = new FansManager;
-#if SUPPORT_ROLAND
- roland = new Roland(*platform);
-#endif
#if SUPPORT_SCANNER
scanner = new Scanner(*platform);
#endif
#if SUPPORT_IOBITS
portControl = new PortControl();
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
display = new Display();
#endif
#if SUPPORT_CAN_EXPANSION
@@ -503,12 +492,10 @@ void RepRap::Init() noexcept
SetPassword(DEFAULT_PASSWORD);
message.Clear();
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
messageSequence = 0;
#endif
- messageBoxMutex.Create("MessageBox");
-
platform->Init();
network->Init();
SetName(DEFAULT_MACHINE_NAME); // Network must be initialised before calling this because this calls SetHostName
@@ -522,16 +509,13 @@ void RepRap::Init() noexcept
printMonitor->Init();
FilamentMonitor::InitStatic();
-#if SUPPORT_ROLAND
- roland->Init();
-#endif
#if SUPPORT_SCANNER
scanner->Init();
#endif
#if SUPPORT_IOBITS
portControl->Init();
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
display->Init();
#endif
#ifdef DUET3_ATE
@@ -722,7 +706,7 @@ void RepRap::Exit() noexcept
#if SUPPORT_IOBITS
portControl->Exit();
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
display->Exit();
#endif
network->Exit();
@@ -749,12 +733,6 @@ void RepRap::Spin() noexcept
spinningModule = moduleGcodes;
gCodes->Spin();
-#if SUPPORT_ROLAND
- ticksInSpinState = 0;
- spinningModule = moduleRoland;
- roland->Spin();
-#endif
-
#if SUPPORT_SCANNER && !SCANNER_AS_SEPARATE_TASK
ticksInSpinState = 0;
spinningModule = moduleScanner;
@@ -769,7 +747,7 @@ void RepRap::Spin() noexcept
spinningModule = moduleFilamentSensors;
FilamentMonitor::Spin();
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
ticksInSpinState = 0;
spinningModule = moduleDisplay;
display->Spin();
@@ -821,14 +799,9 @@ void RepRap::Spin() noexcept
// Check if we need to display a cold extrusion warning
if (now - lastWarningMillis >= MinimumWarningInterval)
{
- ReadLocker lock(toolListLock);
- for (Tool *t = toolList; t != nullptr; t = t->Next())
+ if (Tool::DisplayColdExtrusionWarnings())
{
- if (t->DisplayColdExtrudeWarning())
- {
- platform->MessageF(WarningMessage, "Tool %d was not driven because its heater temperatures were not high enough or it has a heater fault\n", t->myNumber);
- lastWarningMillis = now;
- }
+ lastWarningMillis = now;
}
}
@@ -839,15 +812,7 @@ void RepRap::Spin() noexcept
StateUpdated();
}
- // Check if the message box can be hidden
- {
- MutexLocker lock(messageBoxMutex);
- if (mbox.active && mbox.timer != 0 && now - mbox.timer >= mbox.timeout)
- {
- mbox.active = false;
- StateUpdated();
- }
- }
+ CheckMessageBoxTimeout();
// Keep track of the loop time
if (justSentDiagnostics)
@@ -1079,260 +1044,6 @@ void RepRap::PrintDebug(MessageType mt) noexcept
platform->Message(mt, "\n");
}
-// Add a tool.
-// Prior to calling this, delete any existing tool with the same number
-// The tool list is maintained in tool number order.
-void RepRap::AddTool(Tool* tool) noexcept
-{
- WriteLocker lock(toolListLock);
- Tool** t = &toolList;
- while(*t != nullptr && (*t)->Number() < tool->Number())
- {
- t = &((*t)->next);
- }
- tool->next = *t;
- *t = tool;
- tool->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters, numToolsToReport);
- ToolsUpdated();
-}
-
-void RepRap::DeleteTool(int toolNumber) noexcept
-{
- WriteLocker lock(toolListLock);
-
- // Deselect it if necessary
- if (currentTool != nullptr && currentTool->Number() == toolNumber)
- {
- SelectTool(-1, false);
- }
-
- // Purge any references to this tool
- Tool * tool = nullptr;
- for (Tool **t = &toolList; *t != nullptr; t = &((*t)->next))
- {
- if ((*t)->Number() == toolNumber)
- {
- tool = *t;
- *t = tool->next;
-
- // Switch off any associated heaters
- for (size_t i = 0; i < tool->HeaterCount(); i++)
- {
- heat->SwitchOff(tool->GetHeater(i));
- }
-
- break;
- }
- }
-
- // Delete it
- Tool::Delete(tool);
-
- // Update the number of active heaters and extruder drives
- activeExtruders = activeToolHeaters = numToolsToReport = 0;
- for (Tool *t = toolList; t != nullptr; t = t->Next())
- {
- t->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters, numToolsToReport);
- }
- ToolsUpdated();
-}
-
-// Select the specified tool, putting the existing current tool into standby
-void RepRap::SelectTool(int toolNumber, bool simulating) noexcept
-{
- ReadLockedPointer<Tool> const newTool = GetTool(toolNumber);
- if (!simulating && currentTool != nullptr && currentTool != newTool.Ptr())
- {
- currentTool->Standby();
- }
- currentTool = newTool.Ptr(); // must do this first so that Activate() will always work
- if (!simulating && newTool.IsNotNull())
- {
- newTool->Activate();
- }
-}
-
-void RepRap::PrintTool(int toolNumber, const StringRef& reply) const noexcept
-{
- ReadLockedPointer<Tool> const tool = GetTool(toolNumber);
- if (tool.IsNotNull())
- {
- tool->PrintTool(reply);
- }
- else
- {
- reply.copy("Error: Attempt to print details of non-existent tool.\n");
- }
-}
-
-void RepRap::StandbyTool(int toolNumber, bool simulating) noexcept
-{
- ReadLockedPointer<Tool> const tool = GetTool(toolNumber);
- if (tool.IsNotNull())
- {
- if (!simulating)
- {
- tool->Standby();
- }
- if (currentTool == tool.Ptr())
- {
- currentTool = nullptr;
- }
- }
- else
- {
- platform->MessageF(ErrorMessage, "Attempt to standby a non-existent tool: %d\n", toolNumber);
- }
-}
-
-ReadLockedPointer<Tool> RepRap::GetLockedCurrentTool() const noexcept
-{
- ReadLocker lock(toolListLock);
- return ReadLockedPointer<Tool>(lock, currentTool);
-}
-
-ReadLockedPointer<Tool> RepRap::GetTool(int toolNumber) const noexcept
-{
- ReadLocker lock(toolListLock);
- Tool* tool = toolList;
- while (tool != nullptr)
- {
- if (tool->Number() == toolNumber)
- {
- return ReadLockedPointer<Tool>(lock, tool);
- }
- tool = tool->Next();
- }
- return ReadLockedPointer<Tool>(lock, nullptr); // not an error
-}
-
-// Return the current tool number, or -1 if no tool selected
-int RepRap::GetCurrentToolNumber() const noexcept
-{
- return (currentTool == nullptr) ? -1 : currentTool->Number();
-}
-
-// Get the current tool, or failing that the default tool. May return nullptr if we can't
-// Called when a M104 or M109 command doesn't specify a tool number.
-ReadLockedPointer<Tool> RepRap::GetCurrentOrDefaultTool() const noexcept
-{
- ReadLocker lock(toolListLock);
- // If a tool is already selected, use that one, else use the lowest-numbered tool which is the one at the start of the tool list
- return ReadLockedPointer<Tool>(lock, (currentTool != nullptr) ? currentTool : toolList);
-}
-
-// Return the lowest-numbered tool
-ReadLockedPointer<Tool> RepRap::GetFirstTool() const noexcept
-{
- ReadLocker lock(toolListLock);
- return ReadLockedPointer<Tool>(lock, toolList);
-}
-
-bool RepRap::IsHeaterAssignedToTool(int8_t heater) const noexcept
-{
- ReadLocker lock(toolListLock);
- for (Tool *tool = toolList; tool != nullptr; tool = tool->Next())
- {
- for (size_t i = 0; i < tool->HeaterCount(); i++)
- {
- if (tool->GetHeater(i) == heater)
- {
- // It's already in use by some tool
- return true;
- }
- }
- }
-
- return false;
-}
-
-unsigned int RepRap::GetNumberOfContiguousTools() const noexcept
-{
- int numTools = 0;
- ReadLocker lock(toolListLock);
- for (const Tool *t = toolList; t != nullptr && t->Number() == numTools; t = t->Next())
- {
- ++numTools;
- }
- return (unsigned int)numTools;
-}
-
-// Report the temperatures of one tool in M105 format
-void RepRap::ReportToolTemperatures(const StringRef& reply, const Tool *tool, bool includeNumber) const noexcept
-{
- if (tool != nullptr && tool->HeaterCount() != 0)
- {
- if (reply.strlen() != 0)
- {
- reply.cat(' ');
- }
- if (includeNumber)
- {
- reply.catf("T%u", tool->Number());
- }
- else
- {
- reply.cat("T");
- }
-
- Heat& heat = reprap.GetHeat();
- char sep = ':';
- for (size_t i = 0; i < tool->HeaterCount(); ++i)
- {
- const int heater = tool->GetHeater(i);
- reply.catf("%c%.1f /%.1f", sep, (double)heat.GetHeaterTemperature(heater), (double)heat.GetTargetTemperature(heater));
- sep = ' ';
- }
- }
-}
-
-void RepRap::ReportAllToolTemperatures(const StringRef& reply) const noexcept
-{
- ReadLocker lock(toolListLock);
-
- // The following is believed to be compatible with Marlin and Octoprint, based on thread https://github.com/foosel/OctoPrint/issues/2590#issuecomment-385023980
- ReportToolTemperatures(reply, currentTool, false);
-
- for (const Tool *tool = toolList; tool != nullptr; tool = tool->Next())
- {
- ReportToolTemperatures(reply, tool, true);
- }
-}
-
-GCodeResult RepRap::SetAllToolsFirmwareRetraction(GCodeBuffer& gb, const StringRef& reply, OutputBuffer*& outBuf) THROWS(GCodeException)
-{
- GCodeResult rslt = GCodeResult::ok;
- for (Tool *tool = toolList; tool != nullptr && rslt == GCodeResult::ok; tool = tool->Next())
- {
- rslt = tool->SetFirmwareRetraction(gb, reply, outBuf);
- }
- return rslt;
-}
-
-// Get the current axes used as X axis
-AxesBitmap RepRap::GetCurrentXAxes() const noexcept
-{
- return Tool::GetXAxes(currentTool);
-}
-
-// Get the current axes used as Y axis
-AxesBitmap RepRap::GetCurrentYAxes() const noexcept
-{
- return Tool::GetYAxes(currentTool);
-}
-
-// Get the current axes used as the specified axis
-AxesBitmap RepRap::GetCurrentAxisMapping(unsigned int axis) const noexcept
-{
- return Tool::GetAxisMapping(currentTool, axis);
-}
-
-// Set the previous tool number. Inline because it is only called from one place.
-void RepRap::SetPreviousToolNumber() noexcept
-{
- previousToolNumber = (currentTool != nullptr) ? currentTool->Number() : -1;
-}
-
void RepRap::Tick() noexcept
{
// Kicking the watchdog before it has been initialised may trigger it!
@@ -1344,6 +1055,9 @@ void RepRap::Tick() noexcept
WatchdogResetSecondary(); // kick the secondary watchdog
#endif
+#if SUPPORT_DIRECT_LCD
+ display->Tick();
+#endif
if (!stopped)
{
platform->Tick();
@@ -1400,41 +1114,36 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
// First the user coordinates
#if SUPPORT_WORKPLACE_COORDINATES
- response->catf(",\"wpl\":%u,", gCodes->GetWorkplaceCoordinateSystemNumber());
+ response->catf(",\"wpl\":%u,", gCodes->GetPrimaryWorkplaceCoordinateSystemNumber());
#else
response->cat(',');
#endif
- AppendFloatArray(response, "xyz", numVisibleAxes, [this](size_t axis) noexcept { return gCodes->GetUserCoordinate(axis); }, 3);
+ AppendFloatArray(response, "xyz", numVisibleAxes, [this](size_t axis) noexcept { return gCodes->GetUserCoordinate(gCodes->GetPrimaryMovementState(), axis); }, 3);
// Machine coordinates
+ const MovementState& ms = gCodes->GetPrimaryMovementState(); // we only report the primary in this response
response->cat(',');
- AppendFloatArray(response, "machine", numVisibleAxes, [this](size_t axis) noexcept { return move->LiveCoordinate(axis, currentTool); }, 3);
+ AppendFloatArray(response, "machine", numVisibleAxes, [this, &ms](size_t axis) noexcept { return move->LiveCoordinate(axis, ms.currentTool); }, 3);
// Actual extruder positions since power up, last G92 or last M23
response->cat(',');
- AppendFloatArray(response, "extr", GetExtrudersInUse(), [this](size_t extruder) noexcept { return move->LiveCoordinate(ExtruderToLogicalDrive(extruder), currentTool); }, 1);
+ AppendFloatArray(response, "extr", Tool::GetExtrudersInUse(), [this, &ms](size_t extruder) noexcept { return move->LiveCoordinate(ExtruderToLogicalDrive(extruder), ms.currentTool); }, 1);
// Current speeds
response->catf("},\"speeds\":{\"requested\":%.1f,\"top\":%.1f}", (double)move->GetRequestedSpeedMmPerSec(), (double)move->GetTopSpeedMmPerSec());
// Current tool number
- response->catf(",\"currentTool\":%d", GetCurrentToolNumber());
+ response->catf(",\"currentTool\":%d", ms.GetCurrentToolNumber());
// Output notifications
{
const bool sendBeep = ((source == ResponseSource::AUX || !platform->IsAuxEnabled(0) || platform->IsAuxRaw(0)) && beepDuration != 0 && beepFrequency != 0);
const bool sendMessage = !message.IsEmpty();
- float timeLeft = 0.0;
- MutexLocker lock(messageBoxMutex);
-
- if (mbox.active && mbox.timer != 0)
- {
- timeLeft = (float)(mbox.timeout) / 1000.0 - (float)(millis() - mbox.timer) / 1000.0;
- }
-
- if (sendBeep || sendMessage || mbox.active)
+ const ReadLockedPointer<const MessageBox> mbox(GetCurrentMessageBox());
+ const bool mboxActive = mbox.IsNotNull() && mbox->IsLegacyType();
+ if (sendBeep || sendMessage || mboxActive)
{
response->cat(",\"output\":{");
@@ -1442,7 +1151,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
if (sendBeep)
{
response->catf("\"beepDuration\":%u,\"beepFrequency\":%u", beepDuration, beepFrequency);
- if (sendMessage || mbox.active)
+ if (sendMessage || mboxActive)
{
response->cat(',');
}
@@ -1452,17 +1161,17 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
if (sendMessage)
{
response->catf("\"message\":\"%.s\"", message.c_str());
- if (mbox.active)
+ if (mboxActive)
{
response->cat(',');
}
}
// Report message box
- if (mbox.active)
+ if (mboxActive)
{
response->catf("\"msgBox\":{\"msg\":\"%.s\",\"title\":\"%.s\",\"mode\":%d,\"seq\":%" PRIu32 ",\"timeout\":%.1f,\"controls\":%u}",
- mbox.message.c_str(), mbox.title.c_str(), mbox.mode, mbox.seq, (double)timeLeft, (unsigned int)mbox.controls.GetRaw());
+ mbox->GetMessage(), mbox->GetTitle(), mbox->GetMode(), mbox->GetSeq(), (double)mbox->GetTimeLeft(), (unsigned int)mbox->GetControls().GetRaw());
}
response->cat('}');
}
@@ -1491,8 +1200,8 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
}
// Speed and Extrusion factors in %
- response->catf(",\"speedFactor\":%.1f,", (double)(gCodes->GetSpeedFactor() * 100.0));
- AppendFloatArray(response, "extrFactors", GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetExtrusionFactor(extruder) * 100.0; }, 1);
+ response->catf(",\"speedFactor\":%.1f,", (double)(gCodes->GetPrimarySpeedFactor() * 100.0));
+ AppendFloatArray(response, "extrFactors", Tool::GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetExtrusionFactor(extruder) * 100.0; }, 1);
// Z babystepping
response->catf(",\"babystep\":%.3f}", (double)gCodes->GetTotalBabyStepOffset(Z_AXIS));
@@ -1578,10 +1287,10 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
// Tool temperatures
response->cat(",\"tools\":{\"active\":[");
{
- ReadLocker lock(toolListLock);
- for (const Tool *tool = toolList; tool != nullptr; tool = tool->Next())
+ ReadLocker lock(Tool::toolListLock);
+ for (const Tool *tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next())
{
- AppendFloatArray(response, nullptr, tool->heaterCount, [tool](unsigned int n) noexcept { return tool->activeTemperatures[n]; }, 1);
+ AppendFloatArray(response, nullptr, tool->HeaterCount(), [tool](unsigned int n) noexcept { return tool->GetToolHeaterActiveTemperature(n); }, 1);
if (tool->Next() != nullptr)
{
response->cat(',');
@@ -1589,9 +1298,9 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
}
response->cat("],\"standby\":[");
- for (const Tool *tool = toolList; tool != nullptr; tool = tool->Next())
+ for (const Tool *tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next())
{
- AppendFloatArray(response, nullptr, tool->heaterCount, [tool](unsigned int n) noexcept { return tool->standbyTemperatures[n]; }, 1);
+ AppendFloatArray(response, nullptr, tool->HeaterCount(), [tool](unsigned int n) noexcept { return tool->GetToolHeaterStandbyTemperature(n); }, 1);
if (tool->Next() != nullptr)
{
response->cat(',');
@@ -1748,8 +1457,8 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
// Tool Mapping
{
response->cat(",\"tools\":[");
- ReadLocker lock(toolListLock);
- for (Tool *tool = toolList; tool != nullptr; tool = tool->Next())
+ ReadLocker lock(Tool::toolListLock);
+ for (const Tool *tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next())
{
// Number
response->catf("{\"number\":%d,", tool->Number());
@@ -1804,9 +1513,9 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
}
// Spindle (if configured)
- if (tool->spindleNumber > -1)
+ if (tool->GetSpindleNumber() > -1)
{
- response->catf(",\"spindle\":%d,\"spindleRpm\":%" PRIi32, tool->spindleNumber, tool->spindleRpm);
+ response->catf(",\"spindle\":%d,\"spindleRpm\":%" PRIi32, tool->GetSpindleNumber(), tool->GetSpindleRpm());
}
// Offsets
@@ -1852,13 +1561,13 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) con
response->catf(",\"currentLayerTime\":%.1f,", (double)(printMonitor->GetCurrentLayerTime()));
// Raw Extruder Positions
- AppendFloatArray(response, "extrRaw", GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetRawExtruderTotalByDrive(extruder); }, 1);
+ AppendFloatArray(response, "extrRaw", Tool::GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetRawExtruderTotalByDrive(extruder); }, 1);
// Fraction of file printed
response->catf(",\"fractionPrinted\":%.1f", (double)((printMonitor->IsPrinting()) ? (printMonitor->FractionOfFilePrinted() * 100.0) : 0.0));
// Byte position of the file being printed
- response->catf(",\"filePosition\":%lu", gCodes->GetFilePosition());
+ response->catf(",\"filePosition\":%lu", gCodes->GetPrintingFilePosition());
// First Layer Duration is no longer included
@@ -1990,7 +1699,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
const int8_t bedHeater = (MaxBedHeaters > 0) ? heat->GetBedHeater(0) : -1;
ch = ',';
response->catf("[%.1f", (double)((bedHeater == -1) ? 0.0 : heat->GetHeaterTemperature(bedHeater)));
- for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ for (size_t heater = DefaultE0Heater; heater < Tool::GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, (double)(heat->GetHeaterTemperature(heater)));
ch = ',';
@@ -1999,7 +1708,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
// Send the heater active temperatures
response->catf(",\"active\":[%.1f", (double)((bedHeater == -1) ? 0.0 : heat->GetActiveTemperature(bedHeater)));
- for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ for (size_t heater = DefaultE0Heater; heater < Tool::GetToolHeatersInUse(); heater++)
{
response->catf(",%.1f", (double)(heat->GetActiveTemperature(heater)));
}
@@ -2007,7 +1716,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
// Send the heater standby temperatures
response->catf(",\"standby\":[%.1f", (double)((bedHeater == -1) ? 0.0 : heat->GetStandbyTemperature(bedHeater)));
- for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ for (size_t heater = DefaultE0Heater; heater < Tool::GetToolHeatersInUse(); heater++)
{
response->catf(",%.1f", (double)(heat->GetStandbyTemperature(heater)));
}
@@ -2015,7 +1724,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
// Send the heater statuses (0=off, 1=standby, 2=active, 3 = fault)
response->catf(",\"hstat\":[%u", (bedHeater == -1) ? 0 : heat->GetStatus(bedHeater).ToBaseType());
- for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ for (size_t heater = DefaultE0Heater; heater < Tool::GetToolHeatersInUse(); heater++)
{
response->catf(",%u", heat->GetStatus(heater).ToBaseType());
}
@@ -2023,21 +1732,21 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
// User coordinates
const size_t numVisibleAxes = gCodes->GetVisibleAxes();
- AppendFloatArray(response, "pos", numVisibleAxes, [this](size_t axis) noexcept { return gCodes->GetUserCoordinate(axis); }, 3);
+ AppendFloatArray(response, "pos", numVisibleAxes, [this](size_t axis) noexcept { return gCodes->GetUserCoordinate(gCodes->GetPrimaryMovementState(), axis); }, 3);
// Machine coordinates
response->cat(',');
- AppendFloatArray(response, "machine", numVisibleAxes, [this](size_t axis) noexcept { return move->LiveCoordinate(axis, currentTool); }, 3);
+ AppendFloatArray(response, "machine", numVisibleAxes, [this](size_t axis) noexcept { return move->LiveCoordinate(axis, gCodes->GetPrimaryMovementState().currentTool); }, 3);
// Send the speed and extruder override factors
- response->catf(",\"sfactor\":%.1f,", (double)(gCodes->GetSpeedFactor() * 100.0));
- AppendFloatArray(response, "efactor", GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetExtrusionFactor(extruder) * 100.0; }, 1);
+ response->catf(",\"sfactor\":%.1f,", (double)(gCodes->GetPrimarySpeedFactor() * 100.0));
+ AppendFloatArray(response, "efactor", Tool::GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetExtrusionFactor(extruder) * 100.0; }, 1);
// Send the baby stepping offset
response->catf(",\"babystep\":%.03f", (double)(gCodes->GetTotalBabyStepOffset(Z_AXIS)));
// Send the current tool number
- response->catf(",\"tool\":%d", GetCurrentToolNumber());
+ response->catf(",\"tool\":%d", gCodes->GetPrimaryMovementState().GetCurrentToolNumber());
// Send the Z probe value
const auto zp = platform->GetZProbeOrDefault(0);
@@ -2055,7 +1764,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
// Send the fan settings, for PanelDue firmware 1.13 and later
// Currently, PanelDue assumes that the first value is the print cooling fan speed and only uses that one, so send the mapped fan speed first
- response->catf(",\"fanPercent\":[%.1f", (double)(gCodes->GetMappedFanSpeed() * 100.0));
+ response->catf(",\"fanPercent\":[%.1f", (double)(gCodes->GetPrimaryMovementState().virtualFanSpeed * 100.0));
for (size_t i = 0; i < MaxFans; ++i)
{
const float fanValue = fansManager->GetFanValue(i);
@@ -2083,18 +1792,11 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
// Don't send it if we are flashing firmware, because when we flash firmware we send messages directly to PanelDue and we don't want them to get cleared.
if (!gCodes->IsFlashing())
{
- float timeLeft = 0.0;
- MutexLocker lock(messageBoxMutex);
-
- if (mbox.active && mbox.timer != 0)
- {
- timeLeft = (float)(mbox.timeout) / 1000.0 - (float)(millis() - mbox.timer) / 1000.0;
- }
-
- if (mbox.active)
+ const ReadLockedPointer<const MessageBox> mbox(GetCurrentMessageBox());
+ if (mbox.IsNotNull() && mbox->IsLegacyType())
{
response->catf(",\"msgBox.mode\":%d,\"msgBox.seq\":%" PRIu32 ",\"msgBox.timeout\":%.1f,\"msgBox.controls\":%u,\"msgBox.msg\":\"%.s\",\"msgBox.title\":\"%.s\"",
- mbox.mode, mbox.seq, (double)timeLeft, (unsigned int)mbox.controls.GetRaw(), mbox.message.c_str(), mbox.title.c_str());
+ mbox->GetMode(), mbox->GetSeq(), (double)mbox->GetTimeLeft(), (unsigned int)mbox->GetControls().GetRaw(), mbox->GetMessage(), mbox->GetTitle());
}
else
{
@@ -2122,7 +1824,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) const noexc
#else
0,
#endif
- GetNumberOfContiguousTools(), myName.c_str(), FIRMWARE_NAME);
+ Tool::GetNumberOfContiguousTools(), myName.c_str(), FIRMWARE_NAME);
}
response->cat("}\n"); // include a newline to help PanelDue resync
@@ -2623,7 +2325,7 @@ void RepRap::Beep(unsigned int freq, unsigned int ms) noexcept
// If there is an LCD device present, make it beep
bool bleeped = false;
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
if (display->IsPresent())
{
display->Beep(freq, ms);
@@ -2650,7 +2352,7 @@ void RepRap::Beep(unsigned int freq, unsigned int ms) noexcept
void RepRap::SetMessage(const char *msg) noexcept
{
message.copy(msg);
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
++messageSequence;
#endif
StateUpdated();
@@ -2662,29 +2364,6 @@ void RepRap::SetMessage(const char *msg) noexcept
platform->Message(MessageType::LogInfo, msg);
}
-// Display a message box on the web interface
-void RepRap::SetAlert(const char *msg, const char *title, int mode, float timeout, AxesBitmap controls) noexcept
-{
- MutexLocker lock(messageBoxMutex);
- mbox.message.copy(msg);
- mbox.title.copy(title);
- mbox.mode = mode;
- mbox.timer = (timeout <= 0.0) ? 0 : millis();
- mbox.timeout = round(max<float>(timeout, 0.0) * 1000.0);
- mbox.controls = controls;
- mbox.active = true;
- ++mbox.seq;
- StateUpdated();
-}
-
-// Clear pending message box
-void RepRap::ClearAlert() noexcept
-{
- MutexLocker lock(messageBoxMutex);
- mbox.active = false;
- StateUpdated();
-}
-
// Get the status index
size_t RepRap::GetStatusIndex() const noexcept
{
@@ -2771,138 +2450,6 @@ void RepRap::SetName(const char* nm) noexcept
NetworkUpdated();
}
-// Given that we want to extrude/retract the specified extruder drives, check if they are allowed.
-// For each disallowed one, log an error to report later and return a bit in the bitmap.
-// This may be called by an ISR!
-unsigned int RepRap::GetProhibitedExtruderMovements(unsigned int extrusions, unsigned int retractions) noexcept
-{
- if (GetHeat().ColdExtrude())
- {
- return 0;
- }
-
- Tool * const tool = currentTool;
- if (tool == nullptr)
- {
- // This should not happen, but if no tool is selected then don't allow any extruder movement
- return extrusions | retractions;
- }
-
- unsigned int result = 0;
- for (size_t driveNum = 0; driveNum < tool->DriveCount(); driveNum++)
- {
- const unsigned int extruderDrive = (unsigned int)(tool->GetDrive(driveNum));
- const unsigned int mask = 1 << extruderDrive;
- if (extrusions & mask)
- {
- if (!tool->ToolCanDrive(true))
- {
- result |= mask;
- }
- }
- else if (retractions & mask)
- {
- if (!tool->ToolCanDrive(false))
- {
- result |= mask;
- }
- }
- }
-
- return result;
-}
-
-void RepRap::FlagTemperatureFault(int8_t dudHeater) noexcept
-{
- ReadLocker lock(toolListLock);
- if (toolList != nullptr)
- {
- toolList->FlagTemperatureFault(dudHeater);
- }
-}
-
-GCodeResult RepRap::ClearTemperatureFault(int8_t wasDudHeater, const StringRef& reply) noexcept
-{
- const GCodeResult rslt = heat->ResetFault(wasDudHeater, reply);
- ReadLocker lock(toolListLock);
- if (toolList != nullptr)
- {
- toolList->ClearTemperatureFault(wasDudHeater);
- }
- return rslt;
-}
-
-#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
-
-// Save some resume information, returning true if successful
-// We assume that the tool configuration doesn't change, only the temperatures and the mix
-bool RepRap::WriteToolSettings(FileStore *f) noexcept
-{
- // First write the settings of all tools except the current one and the command to select them if they are on standby
- bool ok = true;
- ReadLocker lock(toolListLock);
- for (const Tool *t = toolList; t != nullptr && ok; t = t->Next())
- {
- if (t != currentTool)
- {
- ok = t->WriteSettings(f);
- }
- }
-
- // Finally write the settings of the active tool and the commands to select it. If no current tool, just deselect all tools.
- if (ok)
- {
- if (currentTool == nullptr)
- {
- ok = f->Write("T-1 P0\n");
- }
- else
- {
- ok = currentTool->WriteSettings(f);
- if (ok)
- {
- String<StringLength20> buf;
- buf.printf("T%u P0\n", currentTool->Number());
- ok = f->Write(buf.c_str());
- }
- }
- }
- return ok;
-}
-
-// Save some information in config-override.g
-bool RepRap::WriteToolParameters(FileStore *f, const bool forceWriteOffsets) noexcept
-{
- bool ok = true, written = false;
- ReadLocker lock(toolListLock);
- for (const Tool *t = toolList; ok && t != nullptr; t = t->Next())
- {
- const AxesBitmap axesProbed = t->GetAxisOffsetsProbed();
- if (axesProbed.IsNonEmpty() || forceWriteOffsets)
- {
- String<StringLength256> scratchString;
- if (!written)
- {
- scratchString.copy("; Probed tool offsets\n");
- written = true;
- }
- scratchString.catf("G10 P%d", t->Number());
- for (size_t axis = 0; axis < MaxAxes; ++axis)
- {
- if (forceWriteOffsets || axesProbed.IsBitSet(axis))
- {
- scratchString.catf(" %c%.2f", gCodes->GetAxisLetters()[axis], (double)(t->GetOffset(axis)));
- }
- }
- scratchString.cat('\n');
- ok = f->Write(scratchString.c_str());
- }
- }
- return ok;
-}
-
-#endif
-
// Firmware update operations
#ifdef __LPC17xx__
@@ -2921,6 +2468,17 @@ bool RepRap::CheckFirmwareUpdatePrerequisites(const StringRef& reply, const Stri
reply.printf("Firmware binary \"%s\" not found", firmwareBinaryLocation.c_str());
return false;
}
+#if SAME5x
+ // UF2 files consist of 512 byte blocks with (for the SAME5x) 256 bytes of data per block
+ if ((firmwareFile->Length() / 512) * 256 > FLASH_SIZE)
+#else
+ if (firmwareFile->Length() > IFLASH_SIZE)
+#endif
+ {
+ firmwareFile->Close();
+ reply.printf("Firmware binary \"%s\" is too big for the available flash memory", filenameRef.c_str());
+ return false;
+ }
// Check that the binary looks sensible. The first word is the initial stack pointer, which should be the top of RAM.
uint32_t firstDword;
@@ -2983,7 +2541,7 @@ void RepRap::UpdateFirmware(const char *iapFilename, const char *iapParam) noexc
void RepRap::PrepareToLoadIap() noexcept
{
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
display->UpdatingFirmware(); // put the firmware update message on the display and stop polling the display
#endif
@@ -3164,7 +2722,108 @@ void RepRap::ReportInternalError(const char *file, const char *func, int line) c
platform->MessageF(ErrorMessage, "Internal Error in %s at %s(%d)\n", func, file, line);
}
-#if SUPPORT_12864_LCD
+// Message box functions
+
+ReadLockedPointer<const MessageBox> RepRap::GetCurrentMessageBox() const noexcept
+{
+ ReadLockedPointer<const MessageBox> p(mboxLock, mboxList);
+ if (p.IsNull())
+ {
+ p.Release();
+ }
+ return p;
+}
+
+// Send a message box, which may require an acknowledgement
+// sParam = 0 Just display the message box, optional timeout
+// sParam = 1 As for 0 but display a Close button as well
+// sParam = 2 Display the message box with an OK button, wait for acknowledgement (waiting is set up by the caller)
+// sParam = 3 As for 2 but also display a Cancel button
+// Returns true if we sent the message box, false if we couldn't because there is already a message box
+bool RepRap::SendAlert(MessageType mt, const char *_ecv_array message, const char *_ecv_array title, int sParam, float tParam, AxesBitmap controls, MessageBoxLimits *_ecv_null limits) noexcept
+{
+ WriteLocker lock(mboxLock);
+
+ // Currently we only allow a single outstanding message box, but we may change that in future
+ if (mboxList != nullptr)
+ {
+ return false;
+ }
+
+ if ((mt & (HttpMessage | AuxMessage | LcdMessage | BinaryCodeReplyFlag)) != 0)
+ {
+ MessageBox *mb = new MessageBox(nullptr);
+ mb->Populate(message, title, sParam, tParam, controls, limits);
+ mboxList = mb;
+ StateUpdated();
+ }
+
+ platform->MessageF(MessageType::LogInfo, "M291: - %s - %s", (strlen(title) > 0 ? title : "[no title]"), message);
+
+ mt = (MessageType)(mt & (UsbMessage | TelnetMessage | Aux2Message));
+ if (mt != 0)
+ {
+ if (strlen(title) > 0)
+ {
+ platform->MessageF(mt, "- %s -\n", title);
+ }
+ platform->MessageF(mt, "%s\n", message);
+ if (sParam == 2)
+ {
+ platform->Message(mt, "Send M292 to continue\n");
+ }
+ else if (sParam == 3)
+ {
+ platform->Message(mt, "Send M292 to continue or M292 P1 to cancel\n");
+ }
+ }
+ return true;
+}
+
+bool RepRap::SendSimpleAlert(MessageType mt, const char *_ecv_array message, const char *_ecv_array title) noexcept
+{
+ return SendAlert(mt, message, title, 1, 0.0, AxesBitmap());
+}
+
+// If we have an active message box with the specified sequence number, close it and tell the caller whether it was blocking or not, then return true
+bool RepRap::AcknowledgeMessageBox(uint32_t seq, bool& wasBlocking) noexcept
+{
+ WriteLocker lock(mboxLock);
+
+ // Currently we only allow a single outstanding message box, but we may change that in future
+ MessageBox *mb = mboxList;
+ if (mb != nullptr && (seq == 0 || mb->GetSeq() == seq))
+ {
+ wasBlocking = mb->IsBlocking();
+ mboxList = mb->GetNext();
+ delete mb;
+ StateUpdated();
+ return true;
+ }
+
+ return false;
+}
+
+// Check if the current message box should be timed out
+void RepRap::CheckMessageBoxTimeout() noexcept
+{
+ if (mboxList != nullptr)
+ {
+ WriteLocker locker(mboxLock);
+ MessageBox *mb = mboxList;
+ if (mb != nullptr && mb->HasTimedOut())
+ {
+ const ExpressionValue rslt = mb->GetDefaultValue();
+ const bool canCancel = mb->CanCancel();
+ mboxList = mb->GetNext();
+ delete mb;
+ StateUpdated();
+ gCodes->MessageBoxClosed(canCancel, false, rslt);
+ }
+ }
+}
+
+#if SUPPORT_DIRECT_LCD
const char *RepRap::GetLatestMessage(uint16_t& sequence) const noexcept
{
diff --git a/src/Platform/RepRap.h b/src/Platform/RepRap.h
index 3500fc77..30dc1ed8 100644
--- a/src/Platform/RepRap.h
+++ b/src/Platform/RepRap.h
@@ -26,6 +26,7 @@ Licence: GPL
#include <RTOSIface/RTOSIface.h>
#include <General/function_ref.h>
#include <ObjectModel/GlobalVariables.h>
+#include "MessageBox.h"
#if SUPPORT_CAN_EXPANSION
# include <CAN/ExpansionManager.h>
@@ -38,20 +39,6 @@ enum class ResponseSource
Generic
};
-// Message box data
-struct MessageBox
-{
- bool active;
- String<MaxMessageLength> message;
- String<MaxTitleLength> title;
- int mode;
- uint32_t seq;
- uint32_t timer, timeout;
- AxesBitmap controls;
-
- MessageBox() noexcept : active(false), seq(0) { }
-};
-
typedef Bitmap<uint32_t> DebugFlags;
class RepRap INHERIT_OBJECT_MODEL
@@ -81,31 +68,6 @@ public:
bool CheckPassword(const char* pw) const noexcept;
void SetPassword(const char* pw) noexcept;
- // Tool management
- void AddTool(Tool* t) noexcept;
- void DeleteTool(int toolNumber) noexcept;
- void SelectTool(int toolNumber, bool simulating) noexcept;
- void StandbyTool(int toolNumber, bool simulating) noexcept;
- int GetCurrentToolNumber() const noexcept;
- void SetPreviousToolNumber() noexcept;
- Tool *GetCurrentTool() const noexcept;
- ReadLockedPointer<Tool> GetLockedCurrentTool() const noexcept;
- ReadLockedPointer<Tool> GetTool(int toolNumber) const noexcept;
- ReadLockedPointer<Tool> GetCurrentOrDefaultTool() const noexcept;
- ReadLockedPointer<Tool> GetFirstTool() const noexcept; // Return the lowest-numbered tool
- AxesBitmap GetCurrentXAxes() const noexcept; // Get the current axes used as X axes
- AxesBitmap GetCurrentYAxes() const noexcept; // Get the current axes used as Y axes
- AxesBitmap GetCurrentAxisMapping(unsigned int axis) const noexcept;
- bool IsHeaterAssignedToTool(int8_t heater) const noexcept;
- unsigned int GetNumberOfContiguousTools() const noexcept;
- void ReportAllToolTemperatures(const StringRef& reply) const noexcept;
- GCodeResult SetAllToolsFirmwareRetraction(GCodeBuffer& gb, const StringRef& reply, OutputBuffer*& outBuf) THROWS(GCodeException);
-
- unsigned int GetProhibitedExtruderMovements(unsigned int extrusions, unsigned int retractions) noexcept;
- void PrintTool(int toolNumber, const StringRef& reply) const noexcept;
- void FlagTemperatureFault(int8_t dudHeater) noexcept;
- GCodeResult ClearTemperatureFault(int8_t wasDudHeater, const StringRef& reply) noexcept;
-
Platform& GetPlatform() const noexcept { return *platform; }
Move& GetMove() const noexcept { return *move; }
Heat& GetHeat() const noexcept { return *heat; }
@@ -115,16 +77,19 @@ public:
PrintMonitor& GetPrintMonitor() const noexcept { return *printMonitor; }
FansManager& GetFansManager() const noexcept { return *fansManager; }
-#if SUPPORT_ROLAND
- Roland& GetRoland() const noexcept { return *roland; }
-#endif
+ // Message box functions
+ ReadLockedPointer<const MessageBox> GetCurrentMessageBox() const noexcept;
+ bool SendAlert(MessageType mt, const char *_ecv_array p_message, const char *_ecv_array title, int sParam, float tParam, AxesBitmap controls, MessageBoxLimits *_ecv_null limits = nullptr) noexcept;
+ bool SendSimpleAlert(MessageType mt, const char *_ecv_array p_message, const char *_ecv_array title) noexcept;
+ bool AcknowledgeMessageBox(uint32_t seq, bool& wasBlocking) noexcept;
+ void CheckMessageBoxTimeout() noexcept;
+
#if SUPPORT_IOBITS
PortControl& GetPortControl() const noexcept { return *portControl; }
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
Display& GetDisplay() const noexcept { return *display; }
const char *GetLatestMessage(uint16_t& sequence) const noexcept;
- const MessageBox& GetMessageBox() const noexcept { return mbox; }
#endif
#if HAS_SBC_INTERFACE
bool UsingSbcInterface() const noexcept { return usingSbcInterface; }
@@ -143,9 +108,6 @@ public:
bool SpinTimeoutImminent() const noexcept;
bool IsStopped() const noexcept;
- uint16_t GetExtrudersInUse() const noexcept;
- uint16_t GetToolHeatersInUse() const noexcept;
-
OutputBuffer *GetStatusResponse(uint8_t type, ResponseSource source) const noexcept;
OutputBuffer *GetConfigResponse() noexcept;
OutputBuffer *GetLegacyStatusResponse(uint8_t type, int seq) const noexcept;
@@ -164,13 +126,6 @@ public:
void Beep(unsigned int freq, unsigned int ms) noexcept;
void SetMessage(const char *msg) noexcept;
- void SetAlert(const char *msg, const char *title, int mode, float timeout, AxesBitmap controls) noexcept;
- void ClearAlert() noexcept;
-
-#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
- bool WriteToolSettings(FileStore *f) noexcept; // save some information for the resume file
- bool WriteToolParameters(FileStore *f, const bool forceWriteOffsets) noexcept; // save some information in config-override.g
-#endif
bool IsProcessingConfig() const noexcept { return processingConfig; }
@@ -211,16 +166,9 @@ public:
WriteLockedPointer<VariableSet> GetGlobalVariablesForWriting() noexcept { return globalVariables.GetForWriting(); }
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(boards)
- OBJECT_MODEL_ARRAY(fans)
- OBJECT_MODEL_ARRAY(gpout)
- OBJECT_MODEL_ARRAY(inputs)
- OBJECT_MODEL_ARRAY(spindles)
- OBJECT_MODEL_ARRAY(tools)
- OBJECT_MODEL_ARRAY(restorePoints)
- OBJECT_MODEL_ARRAY(volumes)
- OBJECT_MODEL_ARRAY(volChanges)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
+
+ ReadWriteLock *_ecv_null GetObjectLock(unsigned int tableNumber) const noexcept override;
private:
static void EncodeString(StringRef& response, const char* src, size_t spaceToLeave, bool allowControlChars = false, char prefix = 0) noexcept;
@@ -231,14 +179,11 @@ private:
size_t GetStatusIndex() const noexcept;
char GetStatusCharacter() const noexcept;
const char* GetStatusString() const noexcept;
- void ReportToolTemperatures(const StringRef& reply, const Tool *tool, bool includeNumber) const noexcept;
bool RunStartupFile(const char *filename) noexcept;
static constexpr uint32_t MaxTicksInSpinState = 20000; // timeout before we reset the processor
static constexpr uint32_t HighTicksInSpinState = 16000; // how long before we warn that timeout is approaching
- static ReadWriteLock toolListLock;
-
Platform* platform;
Network* network;
Move* move;
@@ -252,7 +197,7 @@ private:
PortControl *portControl;
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
Display *display;
#endif
@@ -260,29 +205,17 @@ private:
SbcInterface *sbcInterface;
#endif
-#if SUPPORT_ROLAND
- Roland* roland;
-#endif
-
#if SUPPORT_CAN_EXPANSION
ExpansionManager *expansion;
#endif
- mutable Mutex messageBoxMutex; // mutable so that we can lock and release it in const functions
-
uint16_t boardsSeq, directoriesSeq, fansSeq, heatSeq, inputsSeq, jobSeq, moveSeq, globalSeq;
uint16_t networkSeq, scannerSeq, sensorsSeq, spindlesSeq, stateSeq, toolsSeq, volumesSeq;
GlobalVariables globalVariables;
- Tool* toolList; // the tool list is sorted in order of increasing tool number
- Tool* currentTool;
uint32_t lastWarningMillis; // when we last sent a warning message for things that can happen very often
- uint16_t activeExtruders;
- uint16_t activeToolHeaters;
- uint16_t numToolsToReport;
-
uint16_t ticksInSpinState;
uint16_t heatTaskIdleTicks;
uint32_t fastLoop, slowLoop;
@@ -301,13 +234,12 @@ private:
unsigned int beepFrequency, beepDuration;
uint32_t beepTimer;
String<MaxMessageLength> message;
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
uint16_t messageSequence; // used by 12864 display to detect when there is a new message
#endif
- MessageBox mbox; // message box data
-
- int16_t previousToolNumber; // the tool number we were using before the last tool change, or -1 if we weren't using a tool
+ MessageBox *_ecv_null mboxList; // linked list of message boxes
+ mutable ReadWriteLock mboxLock;
// Deferred diagnostics
MessageType diagnosticsDestination;
@@ -327,14 +259,8 @@ private:
extern RepRap reprap;
inline Module RepRap::GetSpinningModule() const noexcept { return spinningModule; }
-
-inline Tool* RepRap::GetCurrentTool() const noexcept { return currentTool; }
-inline uint16_t RepRap::GetExtrudersInUse() const noexcept { return activeExtruders; }
-inline uint16_t RepRap::GetToolHeatersInUse() const noexcept { return activeToolHeaters; }
inline bool RepRap::IsStopped() const noexcept { return stopped; }
-#define REPORT_INTERNAL_ERROR do { reprap.ReportInternalError((__FILE__), (__func__), (__LINE__)); } while(0)
-
#endif
diff --git a/src/Platform/Roland.cpp b/src/Platform/Roland.cpp
deleted file mode 100644
index 8b87e779..00000000
--- a/src/Platform/Roland.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-// This class allows the RepRap firmware to transmit commands to a Roland mill
-// See: http://www.rolanddg.com/product/3d/3d/mdx-20_15/mdx-20_15.html
-// http://altlab.org/d/content/m/pangelo/ideas/rml_command_guide_en_v100.pdf
-
-#include "RepRapFirmware.h"
-
-#if SUPPORT_ROLAND
-
-Roland::Roland(Platform& p) : platform(p)
-{
-}
-
-void Roland::Init()
-{
- pinMode(ROLAND_RTS_PIN, OUTPUT);
- pinMode(ROLAND_CTS_PIN, INPUT);
- digitalWrite(ROLAND_RTS_PIN, HIGH);
-
- sBuffer = new StringRef(buffer, ARRAY_SIZE(buffer));
- sBuffer->Clear();
-
- bufferPointer = 0;
- Zero(true);
- longWait = platform.Time();
- active = false;
-}
-
-void Roland::Spin()
-{
- if (!Active())
- {
- platform.ClassReport(longWait);
- return;
- }
-
- // 'U' is 01010101 in binary (nice for an oscilloscope...)
-
- //SERIAL_AUX2_DEVICE.write('U');
- //SERIAL_AUX2_DEVICE.flush();
- //return;
-
- // Are we sending something to the Roland?
-
- if (Busy()) // Busy means we are sending something
- {
- if (digitalRead(ROLAND_CTS_PIN))
- {
- platform.ClassReport(longWait);
- return;
- }
-
- SERIAL_AUX2_DEVICE.write(buffer[bufferPointer]);
- SERIAL_AUX2_DEVICE.flush();
- bufferPointer++;
- }
- else // Not sending.
- {
- // Finished talking to the Roland
-
- sBuffer->Clear();
- bufferPointer = 0;
-
- // Anything new to do?
-
- EndstopChecks endStopsToCheck;
- uint8_t moveType;
- FilePosition filePos;
- if (reprap.GetGCodes()->ReadMove(move, endStopsToCheck, moveType, filePos))
- {
- move[AXES] = move[DRIVES]; // Roland doesn't have extruders etc.
- ProcessMove();
- }
- }
-
- platform.ClassReport(longWait);
-}
-
-void Roland::Zero(bool feed)
-{
- size_t lim = feed ? AXES + 1 : AXES;
- for(size_t axis = 0; axis < lim; axis++)
- {
- move[axis] = 0.0;
- coordinates[axis] = 0.0;
- oldCoordinates[axis] = 0.0;
- offset[axis] = 0.0;
- }
-
- if (reprap.Debug(moduleGcodes))
- {
- platform.Message(HOST_MESSAGE, "Roland zero\n");
- }
-}
-
-bool Roland::Busy()
-{
- return buffer[bufferPointer] != 0;
-}
-
-bool Roland::ProcessHome()
-{
- if (Busy())
- {
- return false;
- }
-
- sBuffer->copy("H;\n");
- Zero(false);
- if (reprap.Debug(moduleGcodes))
- {
- platform.MessageF(HOST_MESSAGE, "Roland home: %s", buffer);
- }
- return true;
-}
-
-bool Roland::ProcessDwell(long milliseconds)
-{
- if (Busy())
- {
- return false;
- }
-
- sBuffer->printf("W%ld;", milliseconds);
- sBuffer->catf("Z %.4f,%.4f,%.4f;", oldCoordinates[0], oldCoordinates[1], oldCoordinates[2]);
- sBuffer->cat("W0;\n");
- if (reprap.Debug(moduleGcodes))
- {
- platform.MessageF(HOST_MESSAGE, "Roland dwell: %s", buffer);
- }
- return true;
-}
-
-bool Roland::ProcessG92(float v, size_t axis)
-{
- if (Busy())
- {
- return false;
- }
-
- move[axis] = v;
- coordinates[axis] = move[axis]*ROLAND_FACTOR + offset[axis];
- offset[axis] = oldCoordinates[axis];
- oldCoordinates[axis] = coordinates[axis];
- if (reprap.Debug(moduleGcodes))
- {
- platform.Message(HOST_MESSAGE, "Roland G92\n");
- }
- return true;
-}
-
-bool Roland::ProcessSpindle(float rpm)
-{
- if (Busy())
- {
- return false;
- }
-
- if (rpm < 0.5) // Stop
- {
- sBuffer->printf("!MC 0;\n");
- }
- else // Go
- {
- sBuffer->printf("!RC%ld;!MC 1;\n", (long)(rpm + 100.0));
- }
-
- if (reprap.Debug(moduleGcodes))
- {
- platform.MessageF(HOST_MESSAGE, "Roland spindle: %s", buffer);
- }
- return true;
-
-}
-
-void Roland::GetCurrentRolandPosition(float moveBuffer[])
-{
- for(size_t axis = 0; axis < AXES; axis++)
- {
- moveBuffer[axis] = move[axis];
- }
-
- for(size_t axis = AXES; axis < DRIVES; axis++)
- {
- moveBuffer[axis] = 0.0;
- }
-
- moveBuffer[DRIVES] = move[AXES];
-}
-
-void Roland::ProcessMove()
-{
- for(size_t axis = 0; axis < AXES; axis++)
- {
- coordinates[axis] = move[axis] * ROLAND_FACTOR + offset[axis];
- }
- coordinates[AXES] = move[AXES];
-
- // Start with feedrate; For some reason the Roland won't accept more than 4 d.p.
-
- sBuffer->printf("V %.4f;", coordinates[AXES]);
-
- // Now the move
-
- sBuffer->catf("Z %.4f,%.4f,%.4f;\n", coordinates[0], coordinates[1], coordinates[2]);
-
- for(size_t axis = 0; axis <= AXES; axis++)
- {
- oldCoordinates[axis] = coordinates[axis];
- }
-
- if (reprap.Debug(moduleGcodes))
- {
- platform.MessageF(HOST_MESSAGE, "Roland move: %s", buffer);
- }
-}
-
-
-bool Roland::RawWrite(const char* s)
-{
- if (Busy())
- {
- return false;
- }
-
- sBuffer->copy(s);
- sBuffer->cat("\n");
-
- if (reprap.Debug(moduleGcodes))
- {
- platform.MessageF(HOST_MESSAGE, "Roland rawwrite: %s", buffer);
- }
- return true;
-}
-
-bool Roland::Active()
-{
- return active;
-}
-
-void Roland::Activate()
-{
- digitalWrite(ROLAND_RTS_PIN, LOW);
- active = true;
-
- if (reprap.Debug(moduleGcodes))
- {
- platform.Message(HOST_MESSAGE, "Roland started\n");
- }
-}
-
-bool Roland::Deactivate()
-{
- if (Busy())
- {
- return false;
- }
-
- digitalWrite(ROLAND_RTS_PIN, HIGH);
- active = false;
- if (reprap.Debug(moduleGcodes))
- {
- platform.Message(HOST_MESSAGE, "Roland stopped\n");
- }
- return true;
-}
-
-#endif
diff --git a/src/Platform/Roland.h b/src/Platform/Roland.h
deleted file mode 100644
index 3412e770..00000000
--- a/src/Platform/Roland.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************************************************
-
-RepRapFirmware - Roland
-
-This class can interface with a Roland mill (e.g. Roland MDX-20/15) and allows the underlying hardware
-to act as a G-Code proxy, which translates G-Codes to internal Roland commands.
-
------------------------------------------------------------------------------------------------------
-
-Version 0.1
-
-Created on: Oct 14, 2015
-
-Adrian Bowyer
-
-Licence: GPL
-
-****************************************************************************************************/
-
-#ifndef ROLAND_H
-#define ROLAND_H
-
-#if SUPPORT_ROLAND
-
-// This class allows the RepRap firmware to transmit commands to a Roland mill
-// See: http://www.rolanddg.com/product/3d/3d/mdx-20_15/mdx-20_15.html
-// http://altlab.org/d/content/m/pangelo/ideas/rml_command_guide_en_v100.pdf
-
-#include "RepRapFirmware.h"
-#include "Core.h"
-#include "Platform.h"
-
-const float ROLAND_FACTOR = (1.016088061*100.0/2.54); // Roland units are 0.001"
-const size_t ROLAND_BUFFER_SIZE = 50;
-
-class Roland
-{
- public:
- Roland(Platform& p);
- void Init();
- void Spin();
- bool ProcessHome();
- bool ProcessDwell(long milliseconds);
- bool ProcessG92(float v, size_t axis);
- bool ProcessSpindle(float rpm);
- bool RawWrite(const char* s);
- void GetCurrentRolandPosition(float moveBuffer[]);
- bool Active();
- void Activate();
- bool Deactivate();
-
- private:
- void ProcessMove();
- void Zero(bool feed);
- bool Busy();
-
- Platform& platform;
- float longWait;
-
- float move[DRIVES+1];
- float coordinates[AXES+1];
- float oldCoordinates[AXES+1];
- float offset[AXES+1];
- char buffer[ROLAND_BUFFER_SIZE];
- int bufferPointer;
- StringRef *sBuffer;
- bool active;
-};
-
-#endif
-
-#endif
-
-// vim: ts=4:sw=4
diff --git a/src/Platform/StringHandle.cpp b/src/Platform/StringHandle.cpp
new file mode 100644
index 00000000..9a62d6c6
--- /dev/null
+++ b/src/Platform/StringHandle.cpp
@@ -0,0 +1,153 @@
+/*
+ * StringHandle.cpp
+ *
+ * Created on: 12 Jun 2022
+ * Author: David
+ */
+
+#include "StringHandle.h"
+
+// StringHandle members
+// Build a handle from a single null-terminated string
+StringHandle::StringHandle(const char *s) noexcept : StringHandle(s, strlen(s)) { }
+
+// Build a handle from a character array and a length
+StringHandle::StringHandle(const char *s, size_t len) noexcept
+{
+ if (len == 0)
+ {
+ slotPtr = nullptr;
+ }
+ else
+ {
+ WriteLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ InternalAssign(s, len);
+ }
+}
+
+#if 0 // This constructor is currently unused, but may be useful in future
+// Build a handle by concatenating two strings
+StringHandle::StringHandle(const char *s1, const char *s2) noexcept
+{
+ const size_t len = strlen(s1) + strlen(s2);
+ if (len == 0)
+ {
+ slotPtr = nullptr;
+ }
+ else
+ {
+ WriteLocker locker(heapLock); // prevent other tasks modifying the heap
+ IndexSlot * const slot = AllocateHandle();
+ StorageSpace * const space = AllocateSpace(len + 1);
+ SafeStrncpy(space->data, s1, space->length);
+ SafeStrncat(space->data, s2, space->length);
+ slot->storage = space;
+ slot->refCount = 1;
+ slotPtr = slot;
+ }
+}
+#endif
+
+void StringHandle::Assign(const char *s) noexcept
+{
+ Delete();
+ const size_t len = strlen(s);
+ if (len != 0)
+ {
+ WriteLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ InternalAssign(s, len);
+ }
+}
+
+// Assign the string. Caller must hold a write lock on the heap.
+void StringHandle::InternalAssign(const char *s, size_t len) noexcept
+{
+ Heap::IndexSlot * const slot = Heap::AllocateHandle();
+ Heap::StorageSpace * const space = Heap::AllocateSpace(sizeof(Heap::StorageSpace) + len + 1);
+ SafeStrncpy(space->data, s, space->length - sizeof(Heap::StorageSpace));
+ slot->storage = space;
+ slotPtr = slot;
+}
+
+void StringHandle::Delete() noexcept
+{
+ if (slotPtr != nullptr)
+ {
+ ReadLocker locker(Heap::heapLock); // prevent other tasks modifying the heap
+ Heap::DeleteSlot(slotPtr);
+ slotPtr = nullptr; // clear the pointer to the handle entry
+ }
+}
+
+const StringHandle& StringHandle::IncreaseRefCount() const noexcept
+{
+ if (slotPtr != nullptr)
+ {
+ Heap::IncreaseRefCount(slotPtr);
+ }
+ return *this;
+}
+
+ReadLockedPointer<const char> StringHandle::Get() const noexcept
+{
+ if (slotPtr == nullptr)
+ {
+ return ReadLockedPointer<const char>(nullptr, ""); // a null handle means an empty string
+ }
+
+ ReadLocker locker(Heap::heapLock);
+
+#if CHECK_HANDLES
+ Heap::CheckSlotGood(slotPtr);
+#endif
+
+ return ReadLockedPointer<const char>(locker, slotPtr->storage->data);
+}
+
+size_t StringHandle::GetLength() const noexcept
+{
+ if (slotPtr == nullptr)
+ {
+ return 0;
+ }
+
+ ReadLocker locker(Heap::heapLock);
+
+#if CHECK_HANDLES
+ Heap::CheckSlotGood(slotPtr);
+#endif
+
+ return strlen(slotPtr->storage->data);
+}
+
+// AutoStringHandle members
+
+AutoStringHandle::AutoStringHandle(const AutoStringHandle& other) noexcept
+ : StringHandle(other)
+{
+ IncreaseRefCount();
+}
+
+AutoStringHandle::AutoStringHandle(AutoStringHandle&& other) noexcept
+ : StringHandle(other)
+{
+ other.slotPtr = nullptr;
+}
+
+AutoStringHandle& AutoStringHandle::operator=(const AutoStringHandle& other) noexcept
+{
+ if (slotPtr != other.slotPtr)
+ {
+ Delete();
+ slotPtr = other.slotPtr;
+ IncreaseRefCount();
+ }
+ return *this;
+}
+
+AutoStringHandle::~AutoStringHandle()
+{
+ StringHandle::Delete();
+}
+
+// End
diff --git a/src/Platform/StringHandle.h b/src/Platform/StringHandle.h
new file mode 100644
index 00000000..4d6b6536
--- /dev/null
+++ b/src/Platform/StringHandle.h
@@ -0,0 +1,54 @@
+/*
+ * StringHandle.h
+ *
+ * Created on: 12 Jun 2022
+ * Author: David
+ */
+
+#ifndef SRC_PLATFORM_STRINGHANDLE_H_
+#define SRC_PLATFORM_STRINGHANDLE_H_
+
+#include <RepRapFirmware.h>
+#include "Heap.h"
+
+// Note: StringHandle is a union member in ExpressionValue, therefore it must be no larger than 32 bits and it cannot have a non-trivial destructor, copy constructor etc.
+// This means that when an object containing a StringHandle is copied or destroyed, that object must handle the reference count.
+// Classes other than ExpressionValue should use AutoStringHandle instead;
+class StringHandle
+{
+public:
+ StringHandle() noexcept { slotPtr = nullptr; }
+ explicit StringHandle(const char *s) noexcept;
+ StringHandle(const char *s, size_t len) noexcept;
+
+#if 0 // unused
+ StringHandle(const char *s1, const char *s2) noexcept;
+#endif
+
+ ReadLockedPointer<const char> Get() const noexcept;
+ size_t GetLength() const noexcept;
+ void Delete() noexcept;
+ const StringHandle& IncreaseRefCount() const noexcept;
+ bool IsNull() const noexcept { return slotPtr == nullptr; }
+ void Assign(const char *s) noexcept;
+
+protected:
+ void InternalAssign(const char *s, size_t len) noexcept;
+
+ Heap::IndexSlot * null slotPtr;
+};
+
+// Version of StringHandle that updates the reference counts automatically
+class AutoStringHandle : public StringHandle
+{
+public:
+ AutoStringHandle() noexcept : StringHandle() { }
+ explicit AutoStringHandle(const char *s) noexcept : StringHandle(s) { }
+ AutoStringHandle(const char *s, size_t len) noexcept : StringHandle(s, len) { }
+ AutoStringHandle(const AutoStringHandle& other) noexcept;
+ AutoStringHandle(AutoStringHandle&& other) noexcept;
+ AutoStringHandle& operator=(const AutoStringHandle& other) noexcept;
+ ~AutoStringHandle();
+};
+
+#endif /* SRC_PLATFORM_STRINGHANDLE_H_ */
diff --git a/src/PrintMonitor/PrintMonitor.cpp b/src/PrintMonitor/PrintMonitor.cpp
index fc46edee..af10959e 100644
--- a/src/PrintMonitor/PrintMonitor.cpp
+++ b/src/PrintMonitor/PrintMonitor.cpp
@@ -38,41 +38,42 @@ ReadWriteLock PrintMonitor::printMonitorLock;
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(PrintMonitor, __VA_ARGS__)
#define OBJECT_MODEL_FUNC_IF(_condition,...) OBJECT_MODEL_FUNC_IF_BODY(PrintMonitor, _condition,__VA_ARGS__)
-const ObjectModelArrayDescriptor PrintMonitor::filamentArrayDescriptor =
+const ObjectModelArrayTableEntry PrintMonitor::objectModelArrayTable[] =
{
- &printMonitorLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t
- { return ((const PrintMonitor*)self)->printingFileInfo.numFilaments; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
- { return ExpressionValue(((const PrintMonitor*)self)->printingFileInfo.filamentNeeded[context.GetIndex(0)], 1); }
-};
-
-const ObjectModelArrayDescriptor PrintMonitor::thumbnailArrayDescriptor =
-{
- &printMonitorLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t
- {
- size_t count = 0;
- while (count < MaxThumbnails && ((const PrintMonitor*)self)->printingFileInfo.thumbnails[count].IsValid())
+ // 0. Filaments
+ {
+ &printMonitorLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t
+ { return ((const PrintMonitor*)self)->printingFileInfo.numFilaments; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue
+ { return ExpressionValue(((const PrintMonitor*)self)->printingFileInfo.filamentNeeded[context.GetLastIndex()], 1); }
+ },
+ // 1. Thumbnails
+ {
+ &printMonitorLock,
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t
{
- count++;
- }
- return count;
- },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 2); }
+ size_t count = 0;
+ while (count < MaxThumbnails && ((const PrintMonitor*)self)->printingFileInfo.thumbnails[count].IsValid())
+ {
+ count++;
+ }
+ return count;
+ },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 2); }
+ }
};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(PrintMonitor)
+
constexpr ObjectModelTableEntry PrintMonitor::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. Job members
-#if TRACK_OBJECT_NAMES
{ "build", OBJECT_MODEL_FUNC_IF(self->IsPrinting(), self->gCodes.GetBuildObjects(), 0), ObjectModelEntryFlags::live },
-#endif
{ "duration", OBJECT_MODEL_FUNC_IF(self->IsPrinting(), self->GetPrintOrSimulatedDuration()), ObjectModelEntryFlags::live },
{ "file", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none },
- { "filePosition", OBJECT_MODEL_FUNC(self->gCodes.GetFilePosition()), ObjectModelEntryFlags::live },
- { "firstLayerDuration", OBJECT_MODEL_FUNC_NOSELF(nullptr), ObjectModelEntryFlags::obsolete },
+ { "filePosition", OBJECT_MODEL_FUNC(self->gCodes.GetPrintingFilePosition()), ObjectModelEntryFlags::live },
{ "lastDuration", OBJECT_MODEL_FUNC_IF(!self->IsPrinting(), (int32_t)self->gCodes.GetLastDuration()), ObjectModelEntryFlags::none },
{ "lastFileName", OBJECT_MODEL_FUNC_IF(!self->filenameBeingPrinted.IsEmpty() && !self->IsPrinting(), self->filenameBeingPrinted.c_str()), ObjectModelEntryFlags::none },
// TODO Add enum about the last file print here (to replace lastFileAborted, lastFileCancelled, lastFileSimulated)
@@ -84,7 +85,7 @@ constexpr ObjectModelTableEntry PrintMonitor::objectModelTable[] =
{ "warmUpDuration", OBJECT_MODEL_FUNC_IF(self->IsPrinting(), lrintf(self->GetWarmUpDuration())), ObjectModelEntryFlags::live },
// 1. ParsedFileInfo members
- { "filament", OBJECT_MODEL_FUNC_NOSELF(&filamentArrayDescriptor), ObjectModelEntryFlags::none },
+ { "filament", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::none },
{ "fileName", OBJECT_MODEL_FUNC_IF(self->IsPrinting(), self->filenameBeingPrinted.c_str()), ObjectModelEntryFlags::none },
{ "generatedBy", OBJECT_MODEL_FUNC_IF(!self->printingFileInfo.generatedBy.IsEmpty(), self->printingFileInfo.generatedBy.c_str()), ObjectModelEntryFlags::none },
{ "height", OBJECT_MODEL_FUNC(self->printingFileInfo.objectHeight, 2), ObjectModelEntryFlags::none },
@@ -94,7 +95,7 @@ constexpr ObjectModelTableEntry PrintMonitor::objectModelTable[] =
{ "printTime", OBJECT_MODEL_FUNC_IF(self->printingFileInfo.printTime != 0, (int32_t)self->printingFileInfo.printTime), ObjectModelEntryFlags::none },
{ "simulatedTime", OBJECT_MODEL_FUNC_IF(self->printingFileInfo.simulatedTime != 0, (int32_t)self->printingFileInfo.simulatedTime), ObjectModelEntryFlags::none },
{ "size", OBJECT_MODEL_FUNC(self->printingFileInfo.fileSize), ObjectModelEntryFlags::none },
- { "thumbnails", OBJECT_MODEL_FUNC_NOSELF(&thumbnailArrayDescriptor), ObjectModelEntryFlags::none },
+ { "thumbnails", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
// 2. ParsedFileInfo.thumbnails[] members
{ "format", OBJECT_MODEL_FUNC(self->printingFileInfo.thumbnails[context.GetLastIndex()].format.ToString()), ObjectModelEntryFlags::none },
@@ -106,11 +107,10 @@ constexpr ObjectModelTableEntry PrintMonitor::objectModelTable[] =
// 3. TimesLeft members
{ "filament", OBJECT_MODEL_FUNC(self->EstimateTimeLeftAsExpression(filamentBased)), ObjectModelEntryFlags::live },
{ "file", OBJECT_MODEL_FUNC(self->EstimateTimeLeftAsExpression(fileBased)), ObjectModelEntryFlags::live },
- { "layer", OBJECT_MODEL_FUNC_NOSELF(nullptr), ObjectModelEntryFlags::obsolete },
{ "slicer", OBJECT_MODEL_FUNC(self->EstimateTimeLeftAsExpression(slicerBased)), ObjectModelEntryFlags::live },
};
-constexpr uint8_t PrintMonitor::objectModelTableDescriptor[] = { 4, 12 + TRACK_OBJECT_NAMES, 11, 5, 4 };
+constexpr uint8_t PrintMonitor::objectModelTableDescriptor[] = { 4, 12, 11, 5, 3 };
DEFINE_GET_OBJECT_MODEL_TABLE(PrintMonitor)
@@ -233,11 +233,7 @@ void PrintMonitor::Spin() noexcept
// Otherwise collect some stats after a certain period of time
const uint64_t now = millis64();
- if (isPrinting
-#if SUPPORT_ROLAND
- && !reprap.GetRoland()->Active()
-#endif
- && (uint32_t)now - lastUpdateTime > UpdateIntervalMillis)
+ if (isPrinting && (uint32_t)now - lastUpdateTime > UpdateIntervalMillis)
{
if (gCodes.GetPauseState() != PauseState::notPaused)
{
@@ -384,7 +380,7 @@ float PrintMonitor::FractionOfFilePrinted() const noexcept
{
return -1.0;
}
- return (float)gCodes.GetFilePosition() / (float)printingFileInfo.fileSize;
+ return (float)gCodes.GetPrintingFilePosition() / (float)printingFileInfo.fileSize;
}
// Estimate the print time left in seconds on a preset estimation method
diff --git a/src/PrintMonitor/PrintMonitor.h b/src/PrintMonitor/PrintMonitor.h
index ec264f9a..fa267d78 100644
--- a/src/PrintMonitor/PrintMonitor.h
+++ b/src/PrintMonitor/PrintMonitor.h
@@ -65,9 +65,7 @@ public:
void SetSlicerTimeLeft(float seconds) noexcept;
protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(filament)
- OBJECT_MODEL_ARRAY(thumbnail)
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
static constexpr float MinFilamentUsageForEstimation = 0.01; // Minimum per cent of filament to be printed before the filament-based estimation returns values
diff --git a/src/RepRapFirmware.h b/src/RepRapFirmware.h
index ccff0616..c2845247 100644
--- a/src/RepRapFirmware.h
+++ b/src/RepRapFirmware.h
@@ -138,8 +138,13 @@ namespace CanInterface
#include <RRF3Common.h>
#define THROWS(...) // expands to nothing, for providing exception specifications
+
+// Error reporting for functions that are allowed to throw
#define THROW_INTERNAL_ERROR throw GCodeException(-1, -1, "internal error at file " __FILE__ "(%d)", (int32_t)__LINE__)
+// Error reporting for functions that are not allowed to throw
+#define REPORT_INTERNAL_ERROR do { reprap.ReportInternalError((__FILE__), (__func__), (__LINE__)); } while(0)
+
// Assertion mechanism
extern "C" [[noreturn]] void vAssertCalled(uint32_t line, const char *file) noexcept __attribute__((naked));
#define RRF_ASSERT(_expr) do { if (!(_expr)) { vAssertCalled(__LINE__, __FILE__); } } while (false)
@@ -299,7 +304,7 @@ class FansManager;
class PortControl;
#endif
-#if SUPPORT_12864_LCD
+#if SUPPORT_DIRECT_LCD
class Display;
#endif
@@ -315,11 +320,11 @@ class ExpansionManager;
#if SAME70
typedef double floatc_t; // type of matrix element used for calibration
#else
-// We are more memory-constrained on the older processors
+// We are more memory-constrained on the other processors and they don't support double precision
typedef float floatc_t; // type of matrix element used for calibration
#endif
-#ifdef DUET3
+#if defined(DUET3) || defined(DUET3MINI)
typedef Bitmap<uint32_t> AxesBitmap; // Type of a bitmap representing a set of axes, and sometimes extruders too
#else
typedef Bitmap<uint16_t> AxesBitmap; // Type of a bitmap representing a set of axes, and sometimes extruders too
@@ -331,6 +336,7 @@ typedef Bitmap<uint32_t> HeatersBitmap; // Type of a bitmap representing a set
typedef Bitmap<uint16_t> DriverChannelsBitmap; // Type of a bitmap representing a set of drivers that typically have a common cooling fan
typedef Bitmap<uint32_t> InputPortsBitmap; // Type of a bitmap representing a set of input ports
typedef Bitmap<uint32_t> TriggerNumbersBitmap; // Type of a bitmap representing a set of trigger numbers
+typedef Bitmap<uint64_t> ToolNumbersBitmap; // Type of a bitmap representing a set of tool numbers
#if defined(DUET3) || defined(DUET3MINI)
typedef Bitmap<uint64_t> SensorsBitmap;
@@ -346,6 +352,7 @@ static_assert(NumDirectDrivers <= DriversBitmap::MaxBits());
static_assert(MaxSensors <= SensorsBitmap::MaxBits());
static_assert(MaxGpInPorts <= InputPortsBitmap::MaxBits());
static_assert(MaxTriggers <= TriggerNumbersBitmap::MaxBits());
+static_assert(MaxTools <= ToolNumbersBitmap::MaxBits());
typedef uint16_t Pwm_t; // Type of a PWM value when we don't want to use floats
diff --git a/src/SBC/SbcInterface.cpp b/src/SBC/SbcInterface.cpp
index 7513f28c..0a203999 100644
--- a/src/SBC/SbcInterface.cpp
+++ b/src/SBC/SbcInterface.cpp
@@ -1182,7 +1182,7 @@ void SbcInterface::InvalidateResources() noexcept
gb->MacroRequestSent();
}
gb->AbortFile(true, false);
- gb->MessageAcknowledged(true);
+ gb->MessageAcknowledged(true, ExpressionValue());
}
// Abort the print (if applicable)
diff --git a/src/Storage/FileData.h b/src/Storage/FileData.h
index 8cb4d0f3..c5a006cc 100644
--- a/src/Storage/FileData.h
+++ b/src/Storage/FileData.h
@@ -159,6 +159,10 @@ public:
}
}
+#if SUPPORT_ASYNC_MOVES
+ const FileStore *GetUnderlyingFile() const noexcept { return f; }
+#endif
+
private:
FileStore * null f;
diff --git a/src/Storage/FileInfoParser.cpp b/src/Storage/FileInfoParser.cpp
index 1ecc187c..a41d52f1 100644
--- a/src/Storage/FileInfoParser.cpp
+++ b/src/Storage/FileInfoParser.cpp
@@ -487,7 +487,7 @@ bool FileInfoParser::FindHeight(const char* bufp, size_t len) noexcept
return foundHeight;
}
-// Scan the buffer for th total number of layers. The buffer is null-terminated.
+// Scan the buffer for the total number of layers. The buffer is null-terminated.
bool FileInfoParser::FindNumLayers(const char* bufp, size_t len) noexcept
{
static const char* const numLayerStrings[] =
diff --git a/src/Storage/FileStore.cpp b/src/Storage/FileStore.cpp
index 07e72ba5..fdb8b34a 100644
--- a/src/Storage/FileStore.cpp
+++ b/src/Storage/FileStore.cpp
@@ -734,6 +734,24 @@ uint32_t FileStore::ClusterSize() const noexcept
return (usageMode == FileUseMode::readOnly || usageMode == FileUseMode::readWrite) ? file.obj.fs->csize * 512u : 1; // we divide by the cluster size so return 1 not 0 if there is an error
}
+# if SUPPORT_ASYNC_MOVES
+
+// Copy an open file handle to make a duplicate with its own position. Intended for use on files opened in read-only mode.
+// We assume that FatFs doesn't keep a count of open files, so it's OK for us to just make a copy of the FIL structure.
+void FileStore::CopyFrom(const FileStore *f) noexcept
+{
+ usageMode = FileUseMode::readOnly;
+ writeBuffer = nullptr;
+ crc.Reset();
+ calcCrc = false;
+ closeRequested = false;
+ file = f->file;
+ openCount = 1;
+ reprap.VolumesUpdated();
+}
+
+# endif
+
#endif // HAS_MASS_STORAGE
#if 0 // these are not currently used
diff --git a/src/Storage/FileStore.h b/src/Storage/FileStore.h
index 83621d0a..ca0e5c07 100644
--- a/src/Storage/FileStore.h
+++ b/src/Storage/FileStore.h
@@ -79,6 +79,9 @@ public:
bool Invalidate(const FATFS *fs, bool doClose) noexcept; // Invalidate the file if it uses the specified FATFS object
bool IsOpenOn(const FATFS *fs) const noexcept; // Return true if the file is open on the specified file system
bool IsSameFile(const FIL& otherFile) const noexcept; // Return true if the passed file is the same as ours
+# if SUPPORT_ASYNC_MOVES
+ void CopyFrom(const FileStore *f) noexcept; // Copy an open file handle to make a duplicate with its own position
+# endif
# if 0 // not currently used
bool SetClusterMap(uint32_t[]) noexcept; // Provide a cluster map for fast seeking
# endif
diff --git a/src/Storage/MassStorage.cpp b/src/Storage/MassStorage.cpp
index fe50bb89..630e4fce 100644
--- a/src/Storage/MassStorage.cpp
+++ b/src/Storage/MassStorage.cpp
@@ -453,11 +453,11 @@ FileStore* MassStorage::OpenFile(const char* filePath, OpenMode mode, uint32_t p
{
{
MutexLocker lock(fsMutex);
- for (size_t i = 0; i < MAX_FILES; i++)
+ for (FileStore& fs : files)
{
- if (files[i].IsFree())
+ if (fs.IsFree())
{
- FileStore * const ret = (files[i].Open(filePath, mode, preAllocSize)) ? &files[i]: nullptr;
+ FileStore * const ret = (fs.Open(filePath, mode, preAllocSize)) ? &fs: nullptr;
# if HAS_MASS_STORAGE
if (ret != nullptr && (mode == OpenMode::write || mode == OpenMode::writeWithCrc))
{
@@ -472,6 +472,31 @@ FileStore* MassStorage::OpenFile(const char* filePath, OpenMode mode, uint32_t p
return nullptr;
}
+#if SUPPORT_ASYNC_MOVES
+
+// Duplicate a file handle, with the duplicate having its own position in the file. Use only with files opened in read-only mode.
+FileStore *MassStorage::DuplicateOpenHandle(const FileStore *f) noexcept
+{
+ if (f == nullptr)
+ {
+ return nullptr;
+ }
+
+ MutexLocker lock(fsMutex);
+ for (FileStore& fs : files)
+ {
+ if (fs.IsFree())
+ {
+ fs.CopyFrom(f);
+ return &fs;
+ }
+ }
+ reprap.GetPlatform().Message(ErrorMessage, "Max open file count exceeded.\n");
+ return nullptr;
+}
+
+#endif
+
// Close all files
void MassStorage::CloseAllFiles() noexcept
{
diff --git a/src/Storage/MassStorage.h b/src/Storage/MassStorage.h
index 2d351569..d4cd51ea 100644
--- a/src/Storage/MassStorage.h
+++ b/src/Storage/MassStorage.h
@@ -95,6 +95,10 @@ namespace MassStorage
void RecordSimulationTime(const char *_ecv_array printingFilePath, uint32_t simSeconds) noexcept; // Append the simulated printing time to the end of the file
uint16_t GetVolumeSeq(unsigned int volume) noexcept;
+#if SUPPORT_ASYNC_MOVES
+ FileStore *DuplicateOpenHandle(const FileStore *f) noexcept; // Duplicate a file handle, with the duplicate having its own position in the file. Use only when files opened in read-only mode.
+#endif
+
enum class InfoResult : uint8_t
{
badSlot = 0,
diff --git a/src/Tools/Filament.cpp b/src/Tools/Filament.cpp
index b2003ce7..1f3065fe 100644
--- a/src/Tools/Filament.cpp
+++ b/src/Tools/Filament.cpp
@@ -22,21 +22,20 @@ Filament *Filament::filamentList = nullptr;
Filament::Filament(int extr) noexcept : extruder(extr)
{
- strcpy(name, "");
-
+ name.Clear();
next = filamentList;
filamentList = this;
}
void Filament::Load(const char *filamentName) noexcept
{
- SafeStrncpy(name, filamentName, ARRAY_SIZE(name));
+ name.copy(filamentName);
Filament::SaveAssignments();
}
void Filament::Unload() noexcept
{
- strcpy(name, "");
+ name.Clear();
Filament::SaveAssignments();
}
@@ -68,7 +67,7 @@ void Filament::LoadAssignment() noexcept
}
}
- SafeStrncpy(name, filament, ARRAY_SIZE(name));
+ name.copy(filament);
break;
}
}
diff --git a/src/Tools/Filament.h b/src/Tools/Filament.h
index b4e399fb..c70b76db 100644
--- a/src/Tools/Filament.h
+++ b/src/Tools/Filament.h
@@ -19,12 +19,12 @@ public:
Filament(int extr) noexcept;
int GetExtruder() const noexcept { return extruder; } // Returns the assigned extruder drive
- const char *GetName() const noexcept { return name; } // Returns the name of the currently loaded filament
+ const char *GetName() const noexcept { return name.c_str(); } // Returns the name of the currently loaded filament
// TODO: Add support for filament counters, tool restrictions etc.
// These should be stored in a dedicate file per filament directory like /filaments/<material>/filament.json
- bool IsLoaded() const noexcept { return (name[0] != 0); } // Returns true if a valid filament is assigned to this instance
+ bool IsLoaded() const noexcept { return !name.IsEmpty(); } // Returns true if a valid filament is assigned to this instance
void Load(const char *filamentName) noexcept; // Loads filament parameters from the SD card
void Unload() noexcept; // Unloads the current filament
@@ -34,14 +34,14 @@ public:
static Filament *GetFilamentByExtruder(const int extr) noexcept; // Retrieve the Filament instance assigned to the given extruder drive
private:
- static const char * const FilamentAssignmentFile; // In which file the extruder <-> filament assignments are stored
- static const char * const FilamentAssignmentFileComment; // The comment we write at the start of this file to ensure its integrity
+ static const char * const FilamentAssignmentFile; // In which file the extruder <-> filament assignments are stored
+ static const char * const FilamentAssignmentFileComment; // The comment we write at the start of this file to ensure its integrity
static Filament *filamentList;
Filament *next;
int extruder;
- char name[FilamentNameLength];
+ String<FilamentNameLength> name;
};
#endif
diff --git a/src/Tools/Spindle.cpp b/src/Tools/Spindle.cpp
index cffab595..5b45675a 100644
--- a/src/Tools/Spindle.cpp
+++ b/src/Tools/Spindle.cpp
@@ -27,24 +27,24 @@ constexpr ObjectModelTableEntry Spindle::objectModelTable[] =
{ "canReverse", OBJECT_MODEL_FUNC(self->reverseNotForwardPort.IsValid()), ObjectModelEntryFlags::none },
{ "current", OBJECT_MODEL_FUNC((int32_t)self->currentRpm), ObjectModelEntryFlags::live },
{ "frequency", OBJECT_MODEL_FUNC((int32_t)self->frequency), ObjectModelEntryFlags::verbose },
+ { "idlePwm", OBJECT_MODEL_FUNC(self->idlePwm, 2), ObjectModelEntryFlags::verbose },
{ "max", OBJECT_MODEL_FUNC((int32_t)self->maxRpm), ObjectModelEntryFlags::verbose },
+ { "maxPwm", OBJECT_MODEL_FUNC(self->maxPwm, 2), ObjectModelEntryFlags::verbose },
{ "min", OBJECT_MODEL_FUNC((int32_t)self->minRpm), ObjectModelEntryFlags::verbose },
+ { "minPwm", OBJECT_MODEL_FUNC(self->minPwm, 2), ObjectModelEntryFlags::verbose },
{ "state", OBJECT_MODEL_FUNC(self->state.ToString()), ObjectModelEntryFlags::live },
};
-constexpr uint8_t Spindle::objectModelTableDescriptor[] = { 1, 7 };
+constexpr uint8_t Spindle::objectModelTableDescriptor[] = { 1, 10 };
DEFINE_GET_OBJECT_MODEL_TABLE(Spindle)
#endif
Spindle::Spindle() noexcept
- : currentRpm(0),
- configuredRpm(0),
- minRpm(DefaultMinSpindleRpm),
- maxRpm(DefaultMaxSpindleRpm),
- frequency(0),
- state(SpindleState::unconfigured)
+ : minPwm(DefaultMinSpindlePwm), maxPwm(DefaultMaxSpindlePwm), idlePwm(DefaultIdleSpindlePwm),
+ currentRpm(0), configuredRpm(0), minRpm(DefaultMinSpindleRpm), maxRpm(DefaultMaxSpindleRpm),
+ frequency(0), state(SpindleState::unconfigured)
{
}
@@ -69,6 +69,25 @@ GCodeResult Spindle::Configure(GCodeBuffer& gb, const StringRef& reply) THROWS(G
pwmPort.SetFrequency(frequency);
}
+ if (gb.Seen('K'))
+ {
+ seen = true;
+ float pwm[3];
+ size_t numValues = 3;
+ gb.GetFloatArray(pwm, numValues, false);
+ if (numValues >= 2)
+ {
+ minPwm = constrain(pwm[0], 0.0F, 1.0F);
+ maxPwm = constrain(pwm[1], max<float>(pwm[0], minPwm), 1.0F);
+ }
+ else
+ {
+ minPwm = DefaultMinSpindlePwm;
+ maxPwm = constrain(pwm[0], 0.0F, 1.0F);
+ }
+ idlePwm = (numValues == 3) ? constrain(pwm[2], 0.0F, 1.0F) : DefaultIdleSpindlePwm;
+ }
+
if (gb.Seen('L'))
{
seen = true;
@@ -110,14 +129,14 @@ void Spindle::SetRpm(uint32_t rpm) noexcept
if (state == SpindleState::stopped || rpm == 0)
{
onOffPort.WriteDigital(false);
- pwmPort.WriteAnalog(0.0);
+ pwmPort.WriteAnalog(idlePwm);
currentRpm = 0; // current rpm is flagged live, so no need to change seqs.spindles
}
else if (state == SpindleState::forward)
{
rpm = constrain<int>(rpm, minRpm, maxRpm);
reverseNotForwardPort.WriteDigital(false);
- pwmPort.WriteAnalog((float)(rpm - minRpm) / (float)(maxRpm - minRpm));
+ pwmPort.WriteAnalog(((float)(rpm - minRpm) / (float)(maxRpm - minRpm)) * (maxPwm - minPwm) + minPwm);
onOffPort.WriteDigital(true);
currentRpm = rpm; // current rpm is flagged live, so no need to change seqs.spindles
}
@@ -125,13 +144,14 @@ void Spindle::SetRpm(uint32_t rpm) noexcept
{
rpm = constrain<int>(-rpm, -maxRpm, -minRpm);
reverseNotForwardPort.WriteDigital(true);
- pwmPort.WriteAnalog((float)(-rpm - minRpm) / (float)(maxRpm - minRpm));
+ pwmPort.WriteAnalog(((float)(-rpm - minRpm) / (float)(maxRpm - minRpm)) * (maxPwm - minPwm) + minPwm);
onOffPort.WriteDigital(true);
currentRpm = -rpm; // current rpm is flagged live, so no need to change seqs.spindles
}
}
-void Spindle::SetState(const SpindleState newState) noexcept {
+void Spindle::SetState(const SpindleState newState) noexcept
+{
if (state != newState)
{
state = newState;
diff --git a/src/Tools/Spindle.h b/src/Tools/Spindle.h
index 69eb30dd..28973e41 100644
--- a/src/Tools/Spindle.h
+++ b/src/Tools/Spindle.h
@@ -21,6 +21,7 @@ private:
void SetRpm(const uint32_t rpm) noexcept;
PwmPort pwmPort, onOffPort, reverseNotForwardPort;
+ float minPwm, maxPwm, idlePwm;
uint32_t currentRpm, configuredRpm, minRpm, maxRpm;
PwmFrequency frequency;
SpindleState state;
diff --git a/src/Tools/Tool.cpp b/src/Tools/Tool.cpp
index 2ff8f43b..244cd06c 100644
--- a/src/Tools/Tool.cpp
+++ b/src/Tools/Tool.cpp
@@ -41,83 +41,81 @@
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Tool, __VA_ARGS__)
-constexpr ObjectModelArrayDescriptor Tool::activeTempsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->activeTemperatures[context.GetLastIndex()], 1); }
-};
-
-constexpr ObjectModelArrayDescriptor Tool::standbyTempsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->standbyTemperatures[context.GetLastIndex()], 1); }
-};
-
-constexpr ObjectModelArrayDescriptor Tool::heatersArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Tool*)self)->heaters[context.GetLastIndex()]); }
-};
-
-constexpr ObjectModelArrayDescriptor Tool::feedForwardArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->heaterFeedForward[context.GetLastIndex()], 3); }
-};
-
-constexpr ObjectModelArrayDescriptor Tool::extrudersArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->driveCount; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Tool*)self)->drives[context.GetLastIndex()]); }
-};
-
-constexpr ObjectModelArrayDescriptor Tool::mixArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->driveCount; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->mix[context.GetLastIndex()], 2); }
-};
-
-constexpr ObjectModelArrayDescriptor Tool::offsetsArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->offset[context.GetLastIndex()], 3); }
+constexpr ObjectModelArrayTableEntry Tool::objectModelArrayTable[] =
+{
+ // 0. Active temperatures
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->activeTemperatures[context.GetLastIndex()], 1); }
+ },
+ // 1. Axes
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->axisMapping[context.GetLastIndex()]); }
+ },
+ // 2 Extruders
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->driveCount; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Tool*)self)->drives[context.GetLastIndex()]); }
+ },
+ // 3. Feedforward
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->heaterFeedForward[context.GetLastIndex()], 3); }
+ },
+ // 4. Heaters
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue((int32_t)((const Tool*)self)->heaters[context.GetLastIndex()]); }
+ },
+ // 5. Mix
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->driveCount; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->mix[context.GetLastIndex()], 2); }
+ },
+ // 6. Offsets
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->offset[context.GetLastIndex()], 3); }
+ },
+ // 7. Standby temperatures
+ {
+ nullptr, // no lock needed
+ [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Tool*)self)->heaterCount; },
+ [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->standbyTemperatures[context.GetLastIndex()], 1); }
+ }
};
-constexpr ObjectModelArrayDescriptor Tool::axesArrayDescriptor =
-{
- nullptr, // no lock needed
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return 2; },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Tool*)self)->axisMapping[context.GetLastIndex()]); }
-};
+DEFINE_GET_OBJECT_MODEL_ARRAY_TABLE(Tool)
constexpr ObjectModelTableEntry Tool::objectModelTable[] =
{
// Within each group, these entries must be in alphabetical order
// 0. Tool members
- { "active", OBJECT_MODEL_FUNC_NOSELF(&activeTempsArrayDescriptor), ObjectModelEntryFlags::live },
- { "axes", OBJECT_MODEL_FUNC_NOSELF(&axesArrayDescriptor), ObjectModelEntryFlags::none },
- { "extruders", OBJECT_MODEL_FUNC_NOSELF(&extrudersArrayDescriptor), ObjectModelEntryFlags::none },
+ { "active", OBJECT_MODEL_FUNC_ARRAY(0), ObjectModelEntryFlags::live },
+ { "axes", OBJECT_MODEL_FUNC_ARRAY(1), ObjectModelEntryFlags::none },
+ { "extruders", OBJECT_MODEL_FUNC_ARRAY(2), ObjectModelEntryFlags::none },
{ "fans", OBJECT_MODEL_FUNC(self->fanMapping), ObjectModelEntryFlags::none },
- { "feedForward", OBJECT_MODEL_FUNC_NOSELF(&feedForwardArrayDescriptor), ObjectModelEntryFlags::none },
+ { "feedForward", OBJECT_MODEL_FUNC_ARRAY(3), ObjectModelEntryFlags::none },
{ "filamentExtruder", OBJECT_MODEL_FUNC((int32_t)self->filamentExtruder), ObjectModelEntryFlags::none },
- { "heaters", OBJECT_MODEL_FUNC_NOSELF(&heatersArrayDescriptor), ObjectModelEntryFlags::none },
+ { "heaters", OBJECT_MODEL_FUNC_ARRAY(4), ObjectModelEntryFlags::none },
{ "isRetracted", OBJECT_MODEL_FUNC(self->IsRetracted()), ObjectModelEntryFlags::live },
- { "mix", OBJECT_MODEL_FUNC_NOSELF(&mixArrayDescriptor), ObjectModelEntryFlags::none },
+ { "mix", OBJECT_MODEL_FUNC_ARRAY(5), ObjectModelEntryFlags::none },
{ "name", OBJECT_MODEL_FUNC(self->name), ObjectModelEntryFlags::none },
{ "number", OBJECT_MODEL_FUNC((int32_t)self->myNumber), ObjectModelEntryFlags::none },
- { "offsets", OBJECT_MODEL_FUNC_NOSELF(&offsetsArrayDescriptor), ObjectModelEntryFlags::none },
+ { "offsets", OBJECT_MODEL_FUNC_ARRAY(6), ObjectModelEntryFlags::none },
{ "offsetsProbed", OBJECT_MODEL_FUNC((int32_t)self->axisOffsetsProbed.GetRaw()), ObjectModelEntryFlags::none },
{ "retraction", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none },
{ "spindle", OBJECT_MODEL_FUNC((int32_t)self->spindleNumber), ObjectModelEntryFlags::none },
{ "spindleRpm", OBJECT_MODEL_FUNC((int32_t)self->spindleRpm), ObjectModelEntryFlags::none },
- { "standby", OBJECT_MODEL_FUNC_NOSELF(&standbyTempsArrayDescriptor), ObjectModelEntryFlags::live },
+ { "standby", OBJECT_MODEL_FUNC_ARRAY(7), ObjectModelEntryFlags::live },
{ "state", OBJECT_MODEL_FUNC(self->state.ToString()), ObjectModelEntryFlags::live },
// 1. Tool.retraction members
@@ -134,6 +132,13 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Tool)
#endif
+ReadWriteLock Tool::toolListLock;
+Tool *Tool::toolList = nullptr;
+ToolNumbersBitmap Tool::prohibitedExtrusionTools;
+uint16_t Tool::activeExtruders = 0;
+uint16_t Tool::activeToolHeaters = 0;
+uint16_t Tool::numToolsToReport = 0;
+
// Create a new tool and return a pointer to it. If an error occurs, put an error message in 'reply' and return nullptr.
/*static*/ Tool *Tool::Create(unsigned int toolNumber, const char *toolName, int32_t d[], size_t dCount, int32_t h[], size_t hCount, AxesBitmap xMap, AxesBitmap yMap, FansBitmap fanMap, int filamentDrive, size_t sCount, int8_t spindleNo, const StringRef& reply) noexcept
{
@@ -221,7 +226,6 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Tool)
t->fanMapping = fanMap;
t->heaterFault = false;
t->axisOffsetsProbed.Clear();
- t->displayColdExtrudeWarning = false;
t->retractLength = DefaultRetractLength;
t->retractExtra = 0.0;
t->retractHop = 0.0;
@@ -259,6 +263,79 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Tool)
return t;
}
+// Add a tool.
+// Prior to calling this, delete any existing tool with the same number
+// The tool list is maintained in tool number order.
+/*static*/ void Tool::AddTool(Tool* tool) noexcept
+{
+ WriteLocker lock(toolListLock);
+ Tool** t = &toolList;
+ while(*t != nullptr && (*t)->Number() < tool->Number())
+ {
+ t = &((*t)->next);
+ }
+ tool->next = *t;
+ *t = tool;
+ tool->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters);
+ reprap.ToolsUpdated();
+}
+
+// Delete a tool. Before calling this, ensure that the tool is not the current tool in any MovementState.
+/*static*/ void Tool::DeleteTool(int toolNumber) noexcept
+{
+ WriteLocker lock(toolListLock);
+
+ // Purge any references to this tool
+ Tool * tool = nullptr;
+ for (Tool **t = &toolList; *t != nullptr; t = &((*t)->next))
+ {
+ if ((*t)->Number() == toolNumber)
+ {
+ tool = *t;
+ *t = tool->next;
+
+ // Switch off any associated heaters
+ for (size_t i = 0; i < tool->HeaterCount(); i++)
+ {
+ reprap.GetHeat().SwitchOff(tool->GetHeater(i));
+ }
+
+ break;
+ }
+ }
+
+ // Delete it
+ Tool::Delete(tool);
+
+ // Update the number of active heaters and extruder drives
+ activeExtruders = activeToolHeaters = numToolsToReport = 0;
+ for (Tool *t = toolList; t != nullptr; t = t->Next())
+ {
+ t->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters);
+ }
+ reprap.ToolsUpdated();
+}
+
+/*static*/ unsigned int Tool::GetNumberOfContiguousTools() noexcept
+{
+ unsigned int numTools = 0;
+ ReadLocker lock(Tool::toolListLock);
+ for (const Tool *t = Tool::GetToolList(); t != nullptr && t->Number() == (int)numTools; t = t->Next())
+ {
+ ++numTools;
+ }
+ return numTools;
+}
+
+// Return the tool with the specified number, or null if it was not found
+/*static*/ ReadLockedPointer<Tool> Tool::GetLockedTool(int toolNumber) noexcept
+{
+ ReadLocker lock(toolListLock);
+ Tool* tool;
+ for (tool = toolList; tool != nullptr && tool->Number() != toolNumber; tool = tool->Next()) { }
+ return ReadLockedPointer<Tool>(lock, tool);
+}
+
/*static*/ AxesBitmap Tool::GetXAxes(const Tool *tool) noexcept
{
return (tool == nullptr) ? DefaultXAxisMapping : tool->axisMapping[0];
@@ -279,6 +356,94 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Tool)
return (tool == nullptr) ? 0.0 : tool->offset[axis];
}
+// Given that we want to extrude/retract the specified extruder drives, check if they are allowed.
+// For each disallowed one, log an error to report later and return a bit in the bitmap.
+// This may be called by an ISR!
+/*static*/ unsigned int Tool::GetProhibitedExtruderMovements(unsigned int extrusions, unsigned int retractions, const Tool *tool) noexcept
+{
+ if (reprap.GetHeat().ColdExtrude())
+ {
+ return 0;
+ }
+
+ if (tool == nullptr)
+ {
+ // This should not happen, but if no tool is selected then don't allow any extruder movement
+ return extrusions | retractions;
+ }
+
+ unsigned int result = 0;
+ for (size_t driveNum = 0; driveNum < tool->DriveCount(); driveNum++)
+ {
+ const unsigned int extruderDrive = (unsigned int)(tool->GetDrive(driveNum));
+ const unsigned int mask = 1 << extruderDrive;
+ if (extrusions & mask)
+ {
+ if (!tool->CanDriveExtruder(true))
+ {
+ result |= mask;
+ }
+ }
+ else if (retractions & mask)
+ {
+ if (!tool->CanDriveExtruder(false))
+ {
+ result |= mask;
+ }
+ }
+ }
+
+ return result;
+}
+
+// If there are any tool numbers flagged foe cold extrusion warnings, display the warning messages, clear them and return true
+/*static*/ bool Tool::DisplayColdExtrusionWarnings() noexcept
+{
+ if (prohibitedExtrusionTools.IsEmpty())
+ {
+ return false;
+ }
+
+ prohibitedExtrusionTools.Iterate
+ ([](unsigned int index, unsigned int count) -> void
+ {
+ reprap.GetPlatform().MessageF(WarningMessage, "Tool %u was not driven because its heater temperatures were not high enough or it has a heater fault\n", index);
+ }
+ );
+ prohibitedExtrusionTools.Clear();
+ return true;
+}
+
+// Test whether the specified heater is used by any tool
+/*static*/ bool Tool::IsHeaterAssignedToTool(int8_t heater) noexcept
+{
+ ReadLocker lock(toolListLock);
+ for (Tool *tool = toolList; tool != nullptr; tool = tool->Next())
+ {
+ for (size_t i = 0; i < tool->HeaterCount(); i++)
+ {
+ if (tool->GetHeater(i) == heater)
+ {
+ // It's already in use by some tool
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/*static*/ GCodeResult Tool::SetAllToolsFirmwareRetraction(GCodeBuffer& gb, const StringRef& reply, OutputBuffer*& outBuf) THROWS(GCodeException)
+{
+ GCodeResult rslt = GCodeResult::ok;
+ ReadLocker lock(toolListLock);
+ for (Tool *tool = toolList; tool != nullptr && rslt == GCodeResult::ok; tool = tool->Next())
+ {
+ rslt = tool->SetFirmwareRetraction(gb, reply, outBuf);
+ }
+ return rslt;
+}
+
void Tool::PrintTool(const StringRef& reply) const noexcept
{
reply.printf("Tool %u - ", myNumber);
@@ -362,33 +527,31 @@ void Tool::PrintTool(const StringRef& reply) const noexcept
reply.catf("; status: %s", (state == ToolState::active) ? "selected" : (state == ToolState::standby) ? "standby" : "off");
}
-// There is a temperature fault on a heater, so disable all tools using that heater.
-// This function must be called for the first entry in the linked list.
-void Tool::FlagTemperatureFault(int8_t heater) noexcept
+/*static*/ void Tool::FlagTemperatureFault(int8_t dudHeater) noexcept
{
- Tool* n = this;
- while (n != nullptr)
+ ReadLocker lock(toolListLock);
+ for (Tool *t = toolList; t != nullptr; t = t->Next())
{
- n->SetTemperatureFault(heater);
- n = n->Next();
+ t->SetTemperatureFault(dudHeater);
}
}
-void Tool::ClearTemperatureFault(int8_t heater) noexcept
+/*static*/ GCodeResult Tool::ClearTemperatureFault(int8_t wasDudHeater, const StringRef& reply) noexcept
{
- Tool* n = this;
- while (n != nullptr)
+ const GCodeResult rslt = reprap.GetHeat().ResetFault(wasDudHeater, reply);
+ ReadLocker lock(toolListLock);
+ for (Tool *t = toolList; t != nullptr; t = t->Next())
{
- n->ResetTemperatureFault(heater);
- n = n->Next();
+ t->ResetTemperatureFault(wasDudHeater);
}
+ return rslt;
}
void Tool::SetTemperatureFault(int8_t dudHeater) noexcept
{
- for (size_t heater = 0; heater < heaterCount; heater++)
+ for (size_t heaterIndex = 0; heaterIndex < heaterCount; heaterIndex++)
{
- if (dudHeater == heaters[heater])
+ if (dudHeater == heaters[heaterIndex])
{
heaterFault = true;
return;
@@ -398,9 +561,9 @@ void Tool::SetTemperatureFault(int8_t dudHeater) noexcept
void Tool::ResetTemperatureFault(int8_t wasDudHeater) noexcept
{
- for (size_t heater = 0; heater < heaterCount; heater++)
+ for (size_t heaterIndex = 0; heaterIndex < heaterCount; heaterIndex++)
{
- if (wasDudHeater == heaters[heater])
+ if (wasDudHeater == heaters[heaterIndex])
{
heaterFault = false;
return;
@@ -410,9 +573,9 @@ void Tool::ResetTemperatureFault(int8_t wasDudHeater) noexcept
bool Tool::AllHeatersAtHighTemperature(bool forExtrusion) const noexcept
{
- for (size_t heater = 0; heater < heaterCount; heater++)
+ for (size_t heaterIndex = 0; heaterIndex < heaterCount; heaterIndex++)
{
- const float temperature = reprap.GetHeat().GetHeaterTemperature(heaters[heater]);
+ const float temperature = reprap.GetHeat().GetHeaterTemperature(heaters[heaterIndex]);
if (temperature < reprap.GetHeat().GetRetractionMinTemp() || (forExtrusion && temperature < reprap.GetHeat().GetExtrusionMinTemp()))
{
return false;
@@ -457,12 +620,11 @@ void Tool::Standby() noexcept
void Tool::HeatersToActiveOrStandby(bool active) const noexcept
{
- const Tool * const currentTool = reprap.GetCurrentTool();
for (size_t heaterIndex = 0; heaterIndex < heaterCount; heaterIndex++)
{
const int heaterNumber = heaters[heaterIndex];
- // Don't switch a heater to active if the active tool is using it and is different from this tool
- if (currentTool == this || currentTool == nullptr || !currentTool->UsesHeater(heaterNumber))
+ // Don't switch a heater to active if an active tool is using it and is different from this tool
+ if (!reprap.GetGCodes().IsHeaterUsedByDifferentCurrentTool(heaterNumber, this))
{
String<StringLength100> message;
GCodeResult ret;
@@ -486,12 +648,11 @@ void Tool::HeatersToActiveOrStandby(bool active) const noexcept
void Tool::HeatersToOff() const noexcept
{
- const Tool * const currentTool = reprap.GetCurrentTool();
for (size_t heaterIndex = 0; heaterIndex < heaterCount; heaterIndex++)
{
const int heaterNumber = heaters[heaterIndex];
- // Don't switch a heater to standby if the active tool is using it and is different from this tool
- if (currentTool == this || currentTool == nullptr || !currentTool->UsesHeater(heaterNumber))
+ // Don't switch a heater off if an active tool is using it and is different from this tool
+ if (!reprap.GetGCodes().IsHeaterUsedByDifferentCurrentTool(heaterNumber, this))
{
reprap.GetHeat().SwitchOff(heaterNumber);
}
@@ -499,19 +660,19 @@ void Tool::HeatersToOff() const noexcept
}
// May be called from ISR
-bool Tool::ToolCanDrive(bool extrude) noexcept
+bool Tool::CanDriveExtruder(bool extrude) const noexcept
{
if (!heaterFault && AllHeatersAtHighTemperature(extrude))
{
return true;
}
- displayColdExtrudeWarning = true;
+ prohibitedExtrusionTools.SetBit(myNumber);
return false;
}
// Update the number of active drives and extruders in use to reflect what this tool uses
-void Tool::UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHeaters, uint16_t &numToolsToReport) const noexcept
+void Tool::UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHeaters) const noexcept
{
for (size_t drive = 0; drive < driveCount; drive++)
{
@@ -521,11 +682,12 @@ void Tool::UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHea
}
}
- for (size_t heater = 0; heater < heaterCount; heater++)
+ for (size_t heaterIndex = 0; heaterIndex < heaterCount; heaterIndex++)
{
- if (!reprap.GetHeat().IsBedOrChamberHeater(heaters[heater]) && heaters[heater] >= numHeaters)
+ const int heaterNumber = heaters[heaterIndex];
+ if (!reprap.GetHeat().IsBedOrChamberHeater(heaterNumber) && heaterNumber >= numHeaters)
{
- numHeaters = heaters[heater] + 1;
+ numHeaters = (uint16_t)heaterNumber + 1;
}
}
@@ -535,13 +697,6 @@ void Tool::UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHea
}
}
-bool Tool::DisplayColdExtrudeWarning() noexcept
-{
- bool result = displayColdExtrudeWarning;
- displayColdExtrudeWarning = false;
- return result;
-}
-
void Tool::DefineMix(const float m[]) noexcept
{
for (size_t drive = 0; drive < driveCount; drive++)
@@ -610,15 +765,20 @@ float Tool::GetToolHeaterStandbyTemperature(size_t heaterNumber) const noexcept
return (heaterNumber < heaterCount) ? standbyTemperatures[heaterNumber] : 0.0;
}
+// Thien is called when M104/109/568 or G10 is used to set the temperature of a heater.
void Tool::SetToolHeaterActiveOrStandbyTemperature(size_t heaterNumber, float temp, bool active) THROWS(GCodeException)
{
if (heaterNumber < heaterCount)
{
- float& relevantTemperature = (active) ? activeTemperatures[heaterNumber] : standbyTemperatures[heaterNumber];
const int8_t heater = heaters[heaterNumber];
- const Tool * const currentTool = reprap.GetCurrentTool();
- const Tool * const lastStandbyTool = reprap.GetHeat().GetLastStandbyTool(heater);
- const bool setHeater = (currentTool == nullptr || currentTool == this || (!active && (lastStandbyTool == nullptr || lastStandbyTool == this)));
+ float& relevantTemperature = (active) ? activeTemperatures[heaterNumber] : standbyTemperatures[heaterNumber];
+ // Check whether in addition to storing the temperature, we need to set the heater active or standby temperature to that temperature as well.
+ // If we are setting the active temperature, only set the heater active temperature if this is a current tool otr no other current tool uses this heater
+ // We set the temperature if this tool is current, or there is no current tool that uses this heater
+ // or we are setting the standby temperature and the heater was either last switched to standby when this tool went to standby or it not in standby.
+ const Tool * lastStandbyTool;
+ const bool setHeater = !reprap.GetGCodes().IsHeaterUsedByDifferentCurrentTool(heater, this)
+ || (!active && ((lastStandbyTool = reprap.GetHeat().GetLastStandbyTool(heater)) == nullptr || lastStandbyTool == this));
if (temp <= NEARLY_ABS_ZERO) // temperatures close to ABS_ZERO turn off the heater
{
relevantTemperature = 0;
@@ -642,7 +802,7 @@ void Tool::SetToolHeaterActiveOrStandbyTemperature(size_t heaterNumber, float te
}
}
-void Tool::SetSpindleRpm(uint32_t rpm) THROWS(GCodeException)
+void Tool::SetSpindleRpm(uint32_t rpm, bool isCurrentTool) THROWS(GCodeException)
{
if (spindleNumber > -1)
{
@@ -660,7 +820,7 @@ void Tool::SetSpindleRpm(uint32_t rpm) THROWS(GCodeException)
else
{
spindleRpm = rpm;
- if (reprap.GetCurrentTool() == this)
+ if (isCurrentTool)
{
spindle.SetConfiguredRpm(spindleRpm, true);
}
diff --git a/src/Tools/Tool.h b/src/Tools/Tool.h
index 0804f72b..fb79b733 100644
--- a/src/Tools/Tool.h
+++ b/src/Tools/Tool.h
@@ -33,6 +33,12 @@ Licence: GPL
#include <General/NamedEnum.h>
#include <General/function_ref.h>
+// Bits for T-code P-parameter to specify which macros are supposed to be run
+constexpr uint8_t TFreeBit = 1u << 0;
+constexpr uint8_t TPreBit = 1u << 1;
+constexpr uint8_t TPostBit = 1u << 2;
+constexpr uint8_t DefaultToolChangeParam = TFreeBit | TPreBit | TPostBit;
+
constexpr size_t ToolNameLength = 32; // maximum allowed length for tool names
NamedEnum(ToolState, uint8_t, off, active, standby);
@@ -42,8 +48,6 @@ class Filament;
class Tool INHERIT_OBJECT_MODEL
{
public:
- friend class RepRap;
-
DECLARE_FREELIST_NEW_DELETE(Tool)
~Tool() override { delete name; }
@@ -57,20 +61,35 @@ public:
AxesBitmap yMap,
FansBitmap fanMap,
int filamentDrive,
- size_t sCount, int8_t spindleNo,
+ size_t sCount,
+ int8_t spindleNo,
const StringRef& reply) noexcept;
static void Delete(Tool *t) noexcept { delete t; }
static AxesBitmap GetXAxes(const Tool *tool) noexcept;
static AxesBitmap GetYAxes(const Tool *tool) noexcept;
static AxesBitmap GetAxisMapping(const Tool *tool, unsigned int axis) noexcept;
static float GetOffset(const Tool *tool, size_t axis) noexcept pre(axis < MaxAxes);
+ static void FlagTemperatureFault(int8_t dudHeater) noexcept;
+ static GCodeResult ClearTemperatureFault(int8_t wasDudHeater, const StringRef& reply) noexcept;
+ static void AddTool(Tool* t) noexcept;
+ static void DeleteTool(int toolNumber) noexcept;
+ static uint16_t GetExtrudersInUse() noexcept { return activeExtruders; }
+ static uint16_t GetToolHeatersInUse() noexcept { return activeToolHeaters; }
+ static uint16_t GetNumToolsToReport() noexcept { return numToolsToReport; }
+ static Tool *GetToolList() noexcept { return toolList; }
+ static ReadLockedPointer<Tool> GetLockedTool(int toolNumber) noexcept;
+ static unsigned int GetNumberOfContiguousTools() noexcept;
+ static unsigned int GetProhibitedExtruderMovements(unsigned int extrusions, unsigned int retractions, const Tool *tool) noexcept;
+ static bool DisplayColdExtrusionWarnings() noexcept;
+ static bool IsHeaterAssignedToTool(int8_t heater) noexcept;
+ static GCodeResult SetAllToolsFirmwareRetraction(GCodeBuffer& gb, const StringRef& reply, OutputBuffer*& outBuf) THROWS(GCodeException);
float GetOffset(size_t axis) const noexcept pre(axis < MaxAxes);
void SetOffset(size_t axis, float offs, bool byProbing) noexcept pre(axis < MaxAxes);
AxesBitmap GetAxisOffsetsProbed() const noexcept { return axisOffsetsProbed; }
size_t DriveCount() const noexcept;
int GetDrive(size_t driveNumber) const noexcept pre(driverNumber < DriveCount());
- bool ToolCanDrive(bool extrude) noexcept;
+ bool CanDriveExtruder(bool extrude) const noexcept;
size_t HeaterCount() const noexcept;
int GetHeater(size_t heaterNumber) const noexcept pre(heaterNumber < HeaterCount());
const char *_ecv_array GetName() const noexcept;
@@ -95,7 +114,7 @@ public:
void SetRetracted(bool b) noexcept { isRetracted = b; }
int8_t GetSpindleNumber() const noexcept { return spindleNumber; }
uint32_t GetSpindleRpm() const noexcept { return spindleRpm; }
- void SetSpindleRpm(uint32_t rpm) THROWS(GCodeException);
+ void SetSpindleRpm(uint32_t rpm, bool isCurrentTool) THROWS(GCodeException);
#if HAS_MASS_STORAGE || HAS_SBC_INTERFACE
bool WriteSettings(FileStore *f) const noexcept; // write the tool's settings to file
@@ -120,27 +139,17 @@ public:
void HeatersToOff() const noexcept;
void HeatersToActiveOrStandby(bool active) const noexcept;
-
void ApplyFeedForward(float extrusionSpeed) const noexcept;
void StopFeedForward() const noexcept;
-protected:
- DECLARE_OBJECT_MODEL
- OBJECT_MODEL_ARRAY(activeTemps)
- OBJECT_MODEL_ARRAY(axes)
- OBJECT_MODEL_ARRAY(extruders)
- OBJECT_MODEL_ARRAY(heaters)
- OBJECT_MODEL_ARRAY(mix)
- OBJECT_MODEL_ARRAY(offsets)
- OBJECT_MODEL_ARRAY(standbyTemps)
- OBJECT_MODEL_ARRAY(feedForward)
-
void Activate() noexcept;
void Standby() noexcept;
- void FlagTemperatureFault(int8_t dudHeater) noexcept;
- void ClearTemperatureFault(int8_t wasDudHeater) noexcept;
- void UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHeaters, uint16_t &numToolsToReport) const noexcept;
- bool DisplayColdExtrudeWarning() noexcept;
+ void UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHeaters) const noexcept;
+
+ static ReadWriteLock toolListLock;
+
+protected:
+ DECLARE_OBJECT_MODEL_WITH_ARRAYS
private:
Tool() noexcept : next(nullptr), filament(nullptr), name(nullptr), state(ToolState::off) { }
@@ -152,6 +161,12 @@ private:
static void ToolUpdated() noexcept { reprap.ToolsUpdated(); } // call this whenever we change a variable that is reported in the OM as non-live
+ static Tool* toolList; // the tool list is sorted in order of increasing tool number
+ static ToolNumbersBitmap prohibitedExtrusionTools;
+ static uint16_t activeExtruders;
+ static uint16_t activeToolHeaters;
+ static uint16_t numToolsToReport;
+
Tool* null next;
Filament *filament;
int filamentExtruder;
@@ -184,7 +199,6 @@ private:
ToolState state;
bool heaterFault;
bool isRetracted; // true if filament has been firmware-retracted
- volatile bool displayColdExtrudeWarning;
};
inline int Tool::GetDrive(size_t driveNumber) const noexcept
diff --git a/src/Version.h b/src/Version.h
index 763cdb84..fa6da9f0 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -10,7 +10,7 @@
#ifndef VERSION
// Note: the complete VERSION string must be in standard version number format and must not contain spaces! This is so that DWC can parse it.
-# define MAIN_VERSION "3.4.3+"
+# define MAIN_VERSION "3.5beta0"
# ifdef USE_CAN0
# define VERSION_SUFFIX "(CAN0)"
# else
diff --git a/src/bossa/BossaFlash.cpp b/src/bossa/BossaFlash.cpp
index c1fbc0b0..f83d78a7 100644
--- a/src/bossa/BossaFlash.cpp
+++ b/src/bossa/BossaFlash.cpp
@@ -34,11 +34,11 @@ BossaFlash::BossaFlash(Samba& samba,
uint32_t pages,
uint32_t size,
uint32_t planes,
- uint32_t lockRegions,
+ uint32_t numLockRegions,
uint32_t user,
uint32_t stack) THROWS(GCodeException)
: _samba(samba), _name(name), _addr(addr), _pages(pages), _size(size),
- _planes(planes), _lockRegions(lockRegions), _user(user), _wordCopy(samba, user)
+ _planes(planes), _numLockRegions(numLockRegions), _user(user), _wordCopy(samba, user)
{
_wordCopy.setWords(size / sizeof(uint32_t));
@@ -51,44 +51,39 @@ BossaFlash::BossaFlash(Samba& samba,
_pageBufferB = _pageBufferA + size;
}
-void
-BossaFlash::setLockRegions(const Vector<bool, 16>& regions) THROWS(GCodeException)
+void BossaFlash::setLockRegions(Bitmap<uint32_t>regions) THROWS(GCodeException)
{
- if (regions.Size() > _lockRegions)
- throw FlashRegionError("Flash::setLockRegions: regions.Size() > _lockRegions");
-
_regions.set(regions);
}
-void
-BossaFlash::setSecurity() noexcept
+#if ORIGINAL_BOSSA_CODE
+
+void BossaFlash::setSecurity() noexcept
{
_security.set(true);
}
-void
-BossaFlash::setBor(bool enable) noexcept
+void BossaFlash::setBor(bool enable) noexcept
{
if (canBor())
_bor.set(enable);
}
-void
-BossaFlash::setBod(bool enable) noexcept
+void BossaFlash::setBod(bool enable) noexcept
{
if (canBod())
_bod.set(enable);
}
-void
-BossaFlash::setBootFlash(bool enable) noexcept
+#endif
+
+void BossaFlash::setBootFlash(bool enable) noexcept
{
if (canBootFlash())
_bootFlash.set(enable);
}
-void
-BossaFlash::loadBuffer(const uint8_t* data, uint16_t bufferSize) THROWS(GCodeException)
+void BossaFlash::loadBuffer(const uint8_t* data, uint16_t bufferSize) THROWS(GCodeException)
{
_samba.write(_onBufferA ? _pageBufferA : _pageBufferB, data, bufferSize);
}
diff --git a/src/bossa/BossaFlash.h b/src/bossa/BossaFlash.h
index 99b66a4e..b647bd93 100644
--- a/src/bossa/BossaFlash.h
+++ b/src/bossa/BossaFlash.h
@@ -69,53 +69,47 @@ public:
uint32_t pages, // Number of pages
uint32_t size, // Page size in bytes
uint32_t planes, // Number of flash planes
- uint32_t p_lockRegions, // Number of flash lock regions
+ uint32_t numLockRegions, // Number of flash lock regions
uint32_t user, // Address in SRAM where the applet and buffers will be placed
uint32_t stack) THROWS(GCodeException); // Address in SRAM where the applet stack will be placed
virtual ~BossaFlash() {}
- const char *_ecv_array name() noexcept { return _name; }
+ const char *_ecv_array name() const noexcept { return _name; }
- virtual uint32_t address() noexcept { return _addr; }
- virtual uint32_t pageSize() noexcept { return _size; }
- virtual uint32_t numPages() noexcept { return _pages; }
- virtual uint32_t numPlanes() noexcept { return _planes; }
- virtual uint32_t totalSize() noexcept { return _size * _pages; }
- virtual uint32_t lockRegions() noexcept { return _lockRegions; }
+ uint32_t address() const noexcept { return _addr; }
+ uint32_t pageSize() const noexcept { return _size; }
+ uint32_t numPages() const noexcept { return _pages; }
+ uint32_t numPlanes() const noexcept { return _planes; }
+ uint32_t totalSize() const noexcept { return _size * _pages; }
+ uint32_t getNumLockRegions() const noexcept { return _numLockRegions; }
virtual void eraseAll(uint32_t offset) THROWS(GCodeException) = 0;
virtual void eraseAuto(bool enable) noexcept = 0;
- virtual Vector<bool, 16> getLockRegions() THROWS(GCodeException) = 0;
- virtual void setLockRegions(const Vector<bool, 16>& regions) THROWS(GCodeException);
+ virtual Bitmap<uint32_t> getLockRegions() THROWS(GCodeException) = 0;
+ virtual void setLockRegions(Bitmap<uint32_t> regions) THROWS(GCodeException);
#if ORIGINAL_BOSSA_CODE
virtual bool getSecurity() = 0;
-#endif
- virtual void setSecurity() noexcept;
-
-#if ORIGINAL_BOSSA_CODE
+ void setSecurity() noexcept;
virtual bool getBod() = 0;
-#endif
- virtual void setBod(bool enable) noexcept;
- virtual bool canBod() noexcept = 0;
-
-#if ORIGINAL_BOSSA_CODE
+ void setBod(bool enable) noexcept;
virtual bool getBor() = 0;
+ void setBor(bool enable) noexcept;
+ virtual bool canBor() const noexcept = 0;
#endif
- virtual void setBor(bool enable) noexcept;
- virtual bool canBor() noexcept = 0;
+ virtual bool canBod() const noexcept = 0;
virtual bool getBootFlash() THROWS(GCodeException) = 0;
- virtual void setBootFlash(bool enable) noexcept;
- virtual bool canBootFlash() noexcept = 0;
+ void setBootFlash(bool enable) noexcept;
+ virtual bool canBootFlash() const noexcept = 0;
virtual void writeOptions() THROWS(GCodeException) = 0;
virtual void writePage(uint32_t page) THROWS(GCodeException) = 0;
virtual void readPage(uint32_t page, uint8_t* data) THROWS(GCodeException) = 0;
- virtual void loadBuffer(const uint8_t* data, uint16_t size) THROWS(GCodeException);
+ void loadBuffer(const uint8_t* data, uint16_t size) THROWS(GCodeException);
protected:
Samba& _samba;
@@ -124,15 +118,18 @@ protected:
uint32_t _pages;
uint32_t _size;
uint32_t _planes;
- uint32_t _lockRegions;
+ uint32_t _numLockRegions;
uint32_t _user;
WordCopyApplet _wordCopy;
FlashOption<bool> _bootFlash;
- FlashOption<Vector<bool, 16>> _regions;
+ FlashOption<Bitmap<uint32_t>> _regions;
+
+#if ORIGINAL_BOSSA_CODE
FlashOption<bool> _bod;
FlashOption<bool> _bor;
FlashOption<bool> _security;
+#endif
bool _onBufferA;
uint32_t _pageBufferA;
diff --git a/src/bossa/Device.cpp b/src/bossa/Device.cpp
index d3abc9e8..ab51285f 100644
--- a/src/bossa/Device.cpp
+++ b/src/bossa/Device.cpp
@@ -26,12 +26,12 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
///////////////////////////////////////////////////////////////////////////////
+
#include "Device.h"
#include "EefcFlash.h"
#if ORIGINAL_BOSSA_CODE
-void
-Device::readChipId(uint32_t& chipId, uint32_t& extChipId)
+void Device::readChipId(uint32_t& chipId, uint32_t& extChipId)
{
if ((chipId = _samba.readWord(0x400e0740)) != 0)
{
@@ -44,8 +44,7 @@ Device::readChipId(uint32_t& chipId, uint32_t& extChipId)
}
#endif
-void
-Device::create() THROWS(GCodeException)
+void Device::create() THROWS(GCodeException)
{
BossaFlash* flashPtr;
#if ORIGINAL_BOSSA_CODE
@@ -635,8 +634,7 @@ Device::create() THROWS(GCodeException)
_flash = flashPtr;
}
-void
-Device::reset() THROWS(GCodeException)
+void Device::reset() noexcept
{
try
{
@@ -696,4 +694,4 @@ Device::reset() THROWS(GCodeException)
}
}
-
+// End
diff --git a/src/bossa/Device.h b/src/bossa/Device.h
index 06054576..914ed7d2 100644
--- a/src/bossa/Device.h
+++ b/src/bossa/Device.h
@@ -85,7 +85,7 @@ public:
BossaFlash *_ecv_from null getFlash() const noexcept { return _flash; }
- void reset() THROWS(GCodeException);
+ void reset() noexcept;
private:
Samba& _samba;
diff --git a/src/bossa/EefcFlash.cpp b/src/bossa/EefcFlash.cpp
index 153e38b7..65ba291b 100644
--- a/src/bossa/EefcFlash.cpp
+++ b/src/bossa/EefcFlash.cpp
@@ -63,19 +63,21 @@ EefcFlash::EefcFlash(Samba& samba,
uint32_t pages,
uint32_t size,
uint32_t planes,
- uint32_t lockRegions,
+ uint32_t numLockRegions,
uint32_t user,
uint32_t stack,
uint32_t regs,
bool canBrownout) THROWS(GCodeException)
- : BossaFlash(samba, name, addr, pages, size, planes, lockRegions, user, stack),
+ : BossaFlash(samba, name, addr, pages, size, planes, numLockRegions, user, stack),
_regs(regs), _canBrownout(canBrownout), _eraseAuto(true)
{
// SAM3 Errata (FWS must be 6)
_samba.writeWord(EEFC0_FMR, 0x6 << 8);
if (planes == 2)
+ {
_samba.writeWord(EEFC1_FMR, 0x6 << 8);
+ }
}
EefcFlash::~EefcFlash()
@@ -96,8 +98,7 @@ EefcFlash::eraseAll(uint32_t offset) THROWS(GCodeException)
writeFCR1(EEFC_FCMD_EA, 0);
}
- // Erase all can take an exceptionally long time on some devices
- // so wait on FSR for up to 30 seconds
+ // Erase all can take an exceptionally long time on some devices so wait on FSR for up to 30 seconds
waitFSR(30);
}
// Else we must do it by pages
@@ -124,47 +125,49 @@ EefcFlash::eraseAll(uint32_t offset) THROWS(GCodeException)
}
}
-void
-EefcFlash::eraseAuto(bool enable) noexcept
+void EefcFlash::eraseAuto(bool enable) noexcept
{
_eraseAuto = enable;
}
-Vector<bool, 16>
-EefcFlash::getLockRegions() THROWS(GCodeException)
+Bitmap<uint32_t> EefcFlash::getLockRegions() THROWS(GCodeException)
{
- Vector<bool, 16> regions(_lockRegions, false);
- uint32_t frr;
- uint32_t bit;
+ Bitmap<uint32_t> regions;
waitFSR();
- for (uint32_t region = 0; region < _lockRegions; region++)
+ for (uint32_t region = 0; region < _numLockRegions; region++)
{
- if (_planes == 2 && region >= _lockRegions / 2)
+ if (_planes == 2 && region >= _numLockRegions / 2)
{
- bit = region - _lockRegions / 2;
+ uint32_t bit = region - _numLockRegions / 2;
writeFCR1(EEFC_FCMD_GLB, 0);
waitFSR();
- frr = readFRR1();
+ uint32_t frr = readFRR1();
while (bit >= 32)
{
frr = readFRR1();
bit -= 32;
}
- regions[region] = (frr & (1 << bit)) != 0;
+ if ((frr & (1 << bit)) != 0)
+ {
+ regions.SetBit(region);
+ }
}
else
{
- bit = region;
+ uint32_t bit = region;
writeFCR0(EEFC_FCMD_GLB, 0);
waitFSR();
- frr = readFRR0();
+ uint32_t frr = readFRR0();
while (bit >= 32)
{
frr = readFRR0();
bit -= 32;
}
- regions[region] = (frr & (1 << bit)) != 0;
+ if ((frr & (1 << bit)) != 0)
+ {
+ regions.SetBit(region);
+ }
}
}
@@ -206,8 +209,7 @@ EefcFlash::getBor()
}
#endif
-bool
-EefcFlash::getBootFlash() THROWS(GCodeException)
+bool EefcFlash::getBootFlash() THROWS(GCodeException)
{
waitFSR();
writeFCR0(EEFC_FCMD_GGPB, 0);
@@ -215,8 +217,7 @@ EefcFlash::getBootFlash() THROWS(GCodeException)
return (readFRR0() & (1 << (_canBrownout ? 3 : 1)));
}
-void
-EefcFlash::writeOptions() THROWS(GCodeException)
+void EefcFlash::writeOptions() THROWS(GCodeException)
{
if (canBootFlash() && _bootFlash.isDirty() && _bootFlash.get() != getBootFlash())
{
@@ -237,29 +238,23 @@ EefcFlash::writeOptions() THROWS(GCodeException)
#endif
if (_regions.isDirty())
{
- uint32_t page;
- Vector<bool, 16> current;
-
- if (_regions.get().Size() > _lockRegions)
- throw FlashRegionError("EefcFlash::writeOptions: FlashRegionError");
+ Bitmap<uint32_t> current = getLockRegions();
- current = getLockRegions();
-
- for (uint32_t region = 0; region < _lockRegions; region++)
+ for (uint32_t region = 0; region < _numLockRegions; region++)
{
- if (_regions.get()[region] != current[region])
+ if (_regions.get().IsBitSet(region) != current.IsBitSet(region))
{
- if (_planes == 2 && region >= _lockRegions / 2)
+ if (_planes == 2 && region >= _numLockRegions / 2)
{
- page = (region - _lockRegions / 2) * _pages / _lockRegions;
+ const uint32_t page = (region - _numLockRegions / 2) * _pages / _numLockRegions;
waitFSR();
- writeFCR1(_regions.get()[region] ? EEFC_FCMD_SLB : EEFC_FCMD_CLB, page);
+ writeFCR1(_regions.get().IsBitSet(region) ? EEFC_FCMD_SLB : EEFC_FCMD_CLB, page);
}
else
{
- page = region * _pages / _lockRegions;
+ const uint32_t page = region * _pages / _numLockRegions;
waitFSR();
- writeFCR0(_regions.get()[region] ? EEFC_FCMD_SLB : EEFC_FCMD_CLB, page);
+ writeFCR0(_regions.get().IsBitSet(region) ? EEFC_FCMD_SLB : EEFC_FCMD_CLB, page);
}
}
}
@@ -273,8 +268,7 @@ EefcFlash::writeOptions() THROWS(GCodeException)
#endif
}
-void
-EefcFlash::writePage(uint32_t page) THROWS(GCodeException)
+void EefcFlash::writePage(uint32_t page) THROWS(GCodeException)
{
if (page >= _pages)
throw FlashPageError("EefcFlash::writePage: FlashPageError");
@@ -285,13 +279,16 @@ EefcFlash::writePage(uint32_t page) THROWS(GCodeException)
waitFSR();
_wordCopy.runv();
if (_planes == 2 && page >= _pages / 2)
+ {
writeFCR1(_eraseAuto ? EEFC_FCMD_EWP : EEFC_FCMD_WP, page - _pages / 2);
+ }
else
+ {
writeFCR0(_eraseAuto ? EEFC_FCMD_EWP : EEFC_FCMD_WP, page);
+ }
}
-void
-EefcFlash::readPage(uint32_t page, uint8_t* data) THROWS(GCodeException)
+void EefcFlash::readPage(uint32_t page, uint8_t* data) THROWS(GCodeException)
{
if (page >= _pages)
throw FlashPageError("EefcFlash::readPage: FlashPageError");
@@ -306,16 +303,14 @@ EefcFlash::readPage(uint32_t page, uint8_t* data) THROWS(GCodeException)
_samba.read(_onBufferA ? _pageBufferA : _pageBufferB, data, _size);
}
-void
-EefcFlash::waitFSR(int seconds) THROWS(GCodeException)
+void EefcFlash::waitFSR(int seconds) THROWS(GCodeException)
{
int tries = seconds * 1000;
- uint32_t fsr0;
uint32_t fsr1 = 0x1;
while (tries-- > 0)
{
- fsr0 = _samba.readWord(EEFC0_FSR);
+ uint32_t fsr0 = _samba.readWord(EEFC0_FSR);
if (fsr0 & 0x2)
throw FlashCmdError("EefcFlash::waitFSR: FlashCmdError 1");
if (fsr0 & 0x4)
@@ -337,26 +332,22 @@ EefcFlash::waitFSR(int seconds) THROWS(GCodeException)
throw FlashTimeoutError("EefcFlash::waitFSR: FlashTimeoutError");
}
-void
-EefcFlash::writeFCR0(uint8_t cmd, uint32_t arg) THROWS(GCodeException)
+void EefcFlash::writeFCR0(uint8_t cmd, uint32_t arg) THROWS(GCodeException)
{
_samba.writeWord(EEFC0_FCR, (EEFC_KEY << 24) | (arg << 8) | cmd);
}
-void
-EefcFlash::writeFCR1(uint8_t cmd, uint32_t arg) THROWS(GCodeException)
+void EefcFlash::writeFCR1(uint8_t cmd, uint32_t arg) THROWS(GCodeException)
{
_samba.writeWord(EEFC1_FCR, (EEFC_KEY << 24) | (arg << 8) | cmd);
}
-uint32_t
-EefcFlash::readFRR0() THROWS(GCodeException)
+uint32_t EefcFlash::readFRR0() THROWS(GCodeException)
{
return _samba.readWord(EEFC0_FRR);
}
-uint32_t
-EefcFlash::readFRR1() THROWS(GCodeException)
+uint32_t EefcFlash::readFRR1() THROWS(GCodeException)
{
return _samba.readWord(EEFC1_FRR);
}
diff --git a/src/bossa/EefcFlash.h b/src/bossa/EefcFlash.h
index a92241e6..4c277b82 100644
--- a/src/bossa/EefcFlash.h
+++ b/src/bossa/EefcFlash.h
@@ -42,7 +42,7 @@ public:
uint32_t pages,
uint32_t size,
uint32_t planes,
- uint32_t lockRegions,
+ uint32_t numLockRegions,
uint32_t user,
uint32_t stack,
uint32_t regs,
@@ -52,22 +52,18 @@ public:
void eraseAll(uint32_t offset) THROWS(GCodeException) override;
void eraseAuto(bool enable) noexcept override;
- Vector<bool, 16> getLockRegions() THROWS(GCodeException) override;
+ Bitmap<uint32_t> getLockRegions() THROWS(GCodeException) override;
#if ORIGINAL_BOSSA_CODE
bool getSecurity();
-
- bool getBod();
-#endif
- bool canBod() noexcept override { return _canBrownout; }
-
-#if ORIGINAL_BOSSA_CODE
bool getBor();
+ bool getBod();
+ bool canBor() const noexcept override { return _canBrownout; }
#endif
- bool canBor() noexcept override { return _canBrownout; }
+ bool canBod() const noexcept override { return _canBrownout; }
bool getBootFlash() THROWS(GCodeException) override;
- bool canBootFlash() noexcept override { return true; }
+ bool canBootFlash() const noexcept override { return true; }
void writeOptions() THROWS(GCodeException) override;
diff --git a/src/bossa/Flasher.cpp b/src/bossa/Flasher.cpp
index 4b611874..f0f6a44b 100644
--- a/src/bossa/Flasher.cpp
+++ b/src/bossa/Flasher.cpp
@@ -176,7 +176,7 @@ void Flasher::lock(/* string& regionArg, */ bool enable) THROWS(GCodeException)
{
_observer.onStatus("%s all regions\n", enable ? "Lock" : "Unlock");
#endif
- Vector<bool, 16> regions(_flash->lockRegions(), enable);
+ Bitmap<uint32_t> regions = Bitmap<uint32_t>::MakeLowestNBits((enable) ? _flash->getNumLockRegions() : 0);
_flash->setLockRegions(regions);
#if ORIGINAL_BOSSA_CODE
}
diff --git a/src/bossa/SerialPort.h b/src/bossa/SerialPort.h
index e4cc5236..f29cb925 100644
--- a/src/bossa/SerialPort.h
+++ b/src/bossa/SerialPort.h
@@ -57,8 +57,6 @@ public:
StopBit stop = StopBitOne) noexcept = 0;
virtual void close() noexcept = 0;
- virtual bool isUsb() noexcept = 0;
-
virtual int read(uint8_t* data, int size) noexcept = 0;
virtual int write(const uint8_t* data, int size) noexcept = 0;
virtual int get() noexcept = 0;
@@ -66,8 +64,6 @@ public:
virtual bool timeout(int millisecs) noexcept = 0;
virtual void flush() noexcept = 0;
- virtual void setDTR(bool dtr) noexcept = 0;
- virtual void setRTS(bool rts) noexcept = 0;
};
#endif // _SERIALPORT_H
diff --git a/src/libc/errno.c b/src/libc/errno.c
new file mode 100644
index 00000000..0ba05c99
--- /dev/null
+++ b/src/libc/errno.c
@@ -0,0 +1,17 @@
+/*
+ * errno.c
+ *
+ * Created on: 1 Oct 2022
+ * Author: David
+ *
+ * This file replaces the one in newlib in order to avoid pulling the reent struct
+ */
+
+static int globalErrno = 0;
+
+int * __errno () noexcept
+{
+ return &globalErrno;
+}
+
+// End
diff --git a/src/libc/nano-mallocr.c b/src/libc/nano-mallocr.c
index 4b69b6d4..b5c3c1c4 100644
--- a/src/libc/nano-mallocr.c
+++ b/src/libc/nano-mallocr.c
@@ -32,9 +32,13 @@
* Interface documentation refer to malloc.c.
*/
+#if 0 // DC we don't want to pull in stdio
#include <stdio.h>
+#endif
#include <string.h>
+#if 0 // DC we don't want to pull in errno because it pulls in the reent struct
#include <errno.h>
+#endif
#include <malloc.h>
#if 1 // DC
@@ -57,6 +61,7 @@
#include <assert.h>
#else
#define assert(x) ((void)0)
+#include <stdint.h>
extern void vAssertCalled(uint32_t line, const char *file) noexcept;
#endif
@@ -116,9 +121,9 @@ extern void ReleaseMallocMutex();
#else
#define MALLOC_LOCK
#define MALLOC_UNLOCK
-#endif
#define RERRNO errno
+#endif
#define nano_malloc malloc
#define nano_free free
@@ -279,7 +284,9 @@ void * nano_malloc(RARG malloc_size_t s)
if (alloc_size >= MAX_ALLOC_SIZE || alloc_size < s)
{
+#if 0 // DC
RERRNO = ENOMEM;
+#endif
return NULL;
}
@@ -328,7 +335,9 @@ void * nano_malloc(RARG malloc_size_t s)
/* sbrk returns -1 if fail to allocate */
if (r == (void *)-1)
{
+#if 0 // DC
RERRNO = ENOMEM;
+#endif
MALLOC_UNLOCK;
return NULL;
}