Shop OBEX P1 Docs P2 Docs Learn Events
3D Printer G-Code Parsing And The Propeller Board Of Education - Page 3 — Parallax Forums

3D Printer G-Code Parsing And The Propeller Board Of Education

13»

Comments

  • idbruceidbruce Posts: 6,197
    edited 2014-06-04 05:23
    MagIO2
    By the way ... I posted a ConfigReader in obex, which already handles reading a file line by line, parsing the content into an array.

    Okay I downloaded the object and took a brief look at it, and I must say that it caught my interest, however I will have to study it quite a bit more to get a thorough understanding.

    As I see it, either your object or a similar object, can be used for parsing both the G-Code files and Configuration files, and if there are any discrepancies in the management of data, perhaps a simple switch to go from G-Code file parsing to Configuration file parsing.

    I suppose a well defined parsing stategy is of an absolute necessity, but before that can occur, a decision must be made as to whether floating point support should be added. And of course floating point support will most likely be determined by the algorithms being utilized. So I suppose, my first goal should be to find the appropriate algorithms and movement strategies.

    Anyhow, I liked what I saw in your object and it definitely provided food for thought. Thanks for sharing.

    Bruce
  • MagIO2MagIO2 Posts: 2,243
    edited 2014-06-04 13:24
    Strings in flash:
    I assume with serial LCD you mean a text-display with 2 or 4 lines with 16 or 20 characters, correct? This makes a maximum number of 80 characters for the full LCD screen. I also assume that the flash is not much slower than the SD card (where I usually store my strings). So, we talk about a reading speed of at least some kB per second?
    Question1: How fast can you read?
    Question2: How fast can you update the LCD without getting the text to blur?

    So, my answer to your doubts is: The propeller / flash will beat you and your LCD ;o)

    Machine parameters in flash:
    Well, ok, got your point. Maybe a mixture would be the best. Allow an easy update of the parameters via config-file on SD card, but store a set in flash as well. If the file on SD is newer it will be parsed freshly and stored in flash. If the file has already been read, skip the parsing and use the prepared set in flash. The advantage: If the SD is removed the propeller can still boot with a valid set of parameters.

    2 or 3 images:
    Well ... there really is a memory problem with bigger programs. As I already mentioned the FAT32 driver, which allows directory access (Kyes driver), already needs a bunch of HUB RAM. So, it is very unlikely that all the things you want to do fit into a single 32kB HUB RAM image - well at least all the things that I want to do will not fit. On the other hand it is very unlikely that all parts of the program are needed at the same time. This allows to split the program into parts which are placed in different images (*.bin -files). So, if you select something in the menu, the according image is loaded and started. It is possible to have a COG loading another image and start it (see Femto-Basic). Here we also talk about a fraction of a second.

    It is also possible to load a *.bin file of a program into a running environment (as long as there is enough free HUB-RAM) and start it, pass parameters forth and back and let it access for example the serial driver for the LCD. Already did that.

    COG drivers in EEPROM:
    With this subject for the time being I'd like to refere you to my BLOG entries. Only a few more words so far: I already mentioned this approach in several threads, but I think nobody was interested enough to follow this concept. But for me it is best practice and all of my propeller boards have their drivers stored in upper EEPROM. In short words: we have 7 COGs to fill with other stuff than the main program. Usually drivers go there. If all 7 COGs have different tasks and the needed drivers use a good amount of COG RAM you have 7 PASM blocks with a max. size of 2kB (512 longs) that are occupied (total up to 14kB). What I do is, store those PASM blocks in upper EEPROM (with 32kB you can have 15 PASM drivers) and have
    a) a HUB RAM buffer of 2kB
    b) a SPIN code sequence which loads the needed drivers and start the COGs in a sequence
    After initializing the COGs the HUB RAM buffer can be used for other things. So, in total the gain is up to 14kB minus some hundred bytes for the load and start sequence).

    A side-effect is, that the program using this technique can be run on several different hardware setups as long as the driver is available, because the drivers are stored in EEPROM with all the settings (for example which pins to use for the SD card) already being made in the storing process. So, the main programs become a little bit more hardware independent.

    Did I say a few more words? ;o)

    SWAP:
    Yep, another concept I really like to use regularly. Accessing the swap file is easier and faster than FAT file access. Keyword is VirtualMemory. Also mentioned several times here in the forum. CassLan utilized it to write a text editor (Preditor).

    Ok ... I rechecked the code I wrote for proof of concept of the motion control. Unfortunately it also uses the EEPROM driver concept. But it is fairly easy to replace the FullDuplexSerial_NP (NP stands for NO PASM ;o) with an original FullDuplexSerial and deactivate the code for loading the driver from EEPROM.
    The ramping stuff would simply be done in the repeat loop of the function stepper. The current code for the axes only do a bresnham line algorithm. So, it should already be good for g-code which only contains lines.

    StepperTest - Archive [Date 2014.06.04 Time 21.02].zip
  • idbruceidbruce Posts: 6,197
    edited 2014-06-06 00:05
    MagIO2

    I wrote a response yesterday morning, but accidentally closed the window for my reply:( Later this morning or later today, I will type out another response.
  • idbruceidbruce Posts: 6,197
    edited 2014-06-07 06:29
    MagIO2

    I apologize for not responding yet, but I have been going through some troubles.

    I wrote some code for parsing the G-code file, and I got to the point of seperating all of the field entries into seperate data entries. The code is not exactly what I envisioned, but it was working good. Anyhow, I was just adding some comments, so that I could upload the code here, and it stopped working. After fiddling with my code for hours, I finally broke it down to the bare minimum code, only to discover there is now something wrong in between the Propeller Memory Card and SDCard.openFile(STRING("CAL_HOLD.TXT"), "R"), and even perhaps much sooner, such as SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE)

    At this point, I am not exactly sure what went wrong. Perhaps a loose breadboard connect, corrupted SD card, or even a defective PBOE.
    PUB Main
      'Start FullDuplexSerial
      Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
      WAITCNT(CNT + (1 * CLKFREQ))  
      'Start FATEngine
      SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE)
      'Mount Partition
      SDCard.mountPartition(0)
      Terminal.Str(STRING("Hello"))
      Terminal.Tx(13) 
      'Open G-Code File 
      SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
      Terminal.Str(STRING("Hello")) 
      'Unmount Partition
      SDCard.unmountPartition
      'Stop FATEngine
      SDCard.fatEngineStop
      WAITCNT(CNT + (1 * CLKFREQ))  
      Terminal.Stop
    

    If I comment out SDCard.openFile(STRING("CAL_HOLD.TXT"), "R"), both Terminal.Str(STRING("Hello")) output. If I don't comment out the call, only the top Hello outputs. :(

    Good thing, I saved most of my code in a backup copy before destroying all of my hard work :)

    EDIT: After determining that my code was not the source of my troubles (hours of work), I pulled the SD card and checked it on a PC, only to discover that my G-Code file no longer existed. So I reformatted the card and reinstalled the file. The code above now gives me both Hellos :) I am not exactly sure how the file got deleted though.

    EDIT: Okay, so it got deleted again, after editing the the following line from ANDs to ORs.

    IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})

    Apparently, just guessing at this point, the ORs cause the program to hang, and by shutting down the PBOE or reloading EEPROM, the partition is not being properly unmounted. There is a comment in the source code provided with the FAT16/FAT32 Full File System Driver Documentation v1.0, which states:
    Not unmounting the partition may cause bad things to happen.

    I suppose file deletion was one of those bad things :)
  • idbruceidbruce Posts: 6,197
    edited 2014-06-07 09:25
    Well I got it working :)

    Here is a piece of source code that will open up a G-Code file and parse it line by line, seperating the values into 12 different fields, being designated by 12 different letters common to reprap G-Code. Source: http://reprap.org/wiki/G-code

    The various fields are G, M, T, S, P, X, Y, Z, F, R, E, and N.

    In additon to showing the main file, I have included an attachment to maintain the file format and to include the objects utilized.
    CON
      _CLKMODE        = XTAL1 + PLL16X
      _XINFREQ        = 5_000_000
      'Propeller Memory Card Assignments
      DI              = 0 'IO0 / DI / MOSI / CD Pin
      DO              = 1 'IO1 / DO / MISO Pin
      WP              = 2 'IO2 / WP Pin
      HOLD            = 3 'IO3 / HOLD Pin
      SD_CS           = 4 'CS Pin For MicroSD Card Memory
      FLASH_CS        = 5 'CS Pin For Flash Memory
      SRAM_CS         = 6 'CS Pin For SRAM Memory
      CLK             = 7 'CLK Pin
      'Full Duplex Serial
      RX              = 31   
      TX              = 30  
      COMM_MODE       = 0
      COMM_BAUD_RATE  = 57_600
      'FAT Engine Assignment for unused pins or RTC. Please refer to the
      'FAT16/FAT32 Full File System Driver Documentation for proper usage
      NEG_ONE         = -1
       
    VAR
      'G-Code file line characters 
      BYTE bChBuffer[100]
      'The following variables are intended to store G-Code into various fields.
      'Please note the number of fields may change, depending upon which parameters and commands
      'I actually intend to support.  For more information about these fields, please refer to 
      'RepRap G Code Fields at [URL]http://reprap.org/wiki/G-code[/URL]
      BYTE bFieldElement_0[10] 'G Field
      BYTE bFieldElement_1[10] 'M Field 
      BYTE bFieldElement_2[10] 'T Field
      BYTE bFieldElement_3[10] 'S Field
      BYTE bFieldElement_4[10] 'P Field
      BYTE bFieldElement_5[10] 'X Field
      BYTE bFieldElement_6[10] 'Y Field
      BYTE bFieldElement_7[10] 'Z Field
      BYTE bFieldElement_8[10] 'F Field
      BYTE bFieldElement_9[10] 'R Field
      BYTE bFieldElement_10[10] 'E Field
      BYTE bFieldElement_11[10] 'N Field
    OBJ
      SDCard:       "SD-MMC_FATEngine.spin"
      Memory:       "SPI Memory Driver"
      Terminal:     "FullDuplexSerial"
    PUB Main
      'Start FullDuplexSerial
      Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
      WAITCNT(CNT + (1 * CLKFREQ))
      'Start FATEngine
      SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
      'Mount Partition
      SDCard.mountPartition(0)
      'Open G-Code File "CAL_HOLD.TXT" for reading
      SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
      REPEAT
        'Fill bChBuffer with 0s
        BYTEFILL(@bChBuffer, 0, 100)
        'Grab file lines that are not comments or blank
        GetNextValidFileLine
        IF(bChBuffer[0] == 0)
          QUIT
        'Fill in the various field elements
        FillFieldElements
        'Display the various field seperations
        Terminal.Str(STRING("G="))    
        Terminal.Str(@bFieldElement_0)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("M="))
        Terminal.Str(@bFieldElement_1)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("T="))
        Terminal.Str(@bFieldElement_2)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("S="))
        Terminal.Str(@bFieldElement_3)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("P="))
        Terminal.Str(@bFieldElement_4)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("X="))
        Terminal.Str(@bFieldElement_5)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("Y="))
        Terminal.Str(@bFieldElement_6)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("Z="))
        Terminal.Str(@bFieldElement_7)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("F="))
        Terminal.Str(@bFieldElement_8)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("R="))
        Terminal.Str(@bFieldElement_9)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("E="))
        Terminal.Str(@bFieldElement_10)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("N="))
        Terminal.Str(@bFieldElement_11)
        Terminal.Tx(32) {Space}
        'Seperate G-Code field values line by line
        Terminal.Tx(13) {Carriage Return}
      'Unmount Partition
      SDCard.unmountPartition
      'Stop FATEngine
      SDCard.fatEngineStop
      WAITCNT(CNT + (1 * CLKFREQ))
      'Stop FullDuplexSerial  
      Terminal.Stop
    PUB GetNextValidFileLine
      REPEAT
        'Grab a file line as a string  
        SDCard.readString(@bChBuffer, 99)
        'Keep repeating while the following conditions are met,
        'because we don't want or need these lines beginning with
        'these characters.
        IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
          QUIT
    PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
      'Empty the field elements of any possible data
      BYTEFILL(@bFieldElement_0, 0, 10)
      BYTEFILL(@bFieldElement_1, 0, 10)
      BYTEFILL(@bFieldElement_2, 0, 10)
      BYTEFILL(@bFieldElement_3, 0, 10)
      BYTEFILL(@bFieldElement_4, 0, 10)
      BYTEFILL(@bFieldElement_5, 0, 10)
      BYTEFILL(@bFieldElement_6, 0, 10)
      BYTEFILL(@bFieldElement_7, 0, 10)
      BYTEFILL(@bFieldElement_8, 0, 10)
      BYTEFILL(@bFieldElement_9, 0, 10)
      BYTEFILL(@bFieldElement_10, 0, 10)
      BYTEFILL(@bFieldElement_11, 0, 10)
      Index := 0
      ValueIndex := 0
      'Parse file line into seperate field values.  Various fields will remain empty
      REPEAT
        Char := bChBuffer[Index]
          'Exclude these characters from becoming part of are field entries
          IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
            QUIT
          ELSEIF(Char == 32 {Space})
            Index++
            ValueIndex := 0
            NEXT
          ELSEIF(Char == 71 {G Character})
            'Set current field element
            Field_Element := 0
            Index++
            NEXT
          ELSEIF(Char == 77 {M Character})
            'Set current field element
            Field_Element := 1
            Index++
            NEXT
          ELSEIF(Char == 84 {T Character})
            'Set current field element
            Field_Element := 2
            Index++
            NEXT
          ELSEIF(Char == 83 {S Character})
            'Set current field element
            Field_Element := 3
            Index++
            NEXT
          ELSEIF(Char == 80 {P Character})
            'Set current field element
            Field_Element := 4
            Index++
            NEXT
          ELSEIF(Char == 88 {X Character})
            'Set current field element
            Field_Element := 5
            Index++
            NEXT
          ELSEIF(Char == 89 {Y Character})
            'Set current field element
            Field_Element := 6
            Index++
            NEXT
          ELSEIF(Char == 90 {Z Character})
            'Set current field element
            Field_Element := 7
            Index++
            NEXT
          ELSEIF(Char == 70 {F Character})
            'Set current field element
            Field_Element := 8
            Index++
            NEXT
          ELSEIF(Char == 82 {R Character})
            'Set current field element
            Field_Element := 9
            Index++
            NEXT
          ELSEIF(Char == 69 {E Character})
            'Set current field element
            Field_Element := 10
            Index++
            NEXT
          ELSEIF(Char == 78 {N Character})
            'Set current field element
            Field_Element := 11
            Index++
            NEXT
          ELSE
            IF(Field_Element == 0)
              'G Field element character
              bFieldElement_0[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 1)
              'M Field element character
              bFieldElement_1[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 2)
              'T Field element character
              bFieldElement_2[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 3)
              'S Field element character
              bFieldElement_3[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 4)
              'P Field element character
              bFieldElement_4[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 5)
              'X Field element character
              bFieldElement_5[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 6)
              'Y Field element character
              bFieldElement_6[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 7)
              'Z Field element character
              bFieldElement_7[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 8)
              'F Field element character
              bFieldElement_8[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 9)
              'R Field element character
              bFieldElement_9[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 10)
              'E Field element character
              bFieldElement_10[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 11)
              'N Field element character
              bFieldElement_11[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
  • Mike GMike G Posts: 2,702
    edited 2014-06-07 10:20
    You might consider a data structure like the following...
    CON
      #0, G_FIELD, M_FIELD, T_FIELD, S_FIELD, P_FIELD, {
    }     X_FIELD, Y_FIELD, Z_FIELD, F_FIELD, R_FIELD, {
    }     E_FIELD, N_FIELD
    
      FIELD_LEN = 10
      NUMBER_OF_FIELDS = 12
    
    DAT  
      gf  byte   $0[FIELD_LEN]
      mf  byte   $0[FIELD_LEN]
      tf  byte   $0[FIELD_LEN]
      sf  byte   $0[FIELD_LEN]
      pf  byte   $0[FIELD_LEN]
      xf  byte   $0[FIELD_LEN]
      yf  byte   $0[FIELD_LEN]
      zf  byte   $0[FIELD_LEN]
      ff  byte   $0[FIELD_LEN]
      af  byte   $0[FIELD_LEN]
      ef  byte   $0[FIELD_LEN]
      nf  byte   $0[FIELD_LEN]
    
     code long   @gf, @mf, @tf, @sf, @pf, @xf, @yf, @zf, @ff, @af, @ef, @nf
    
  • idbruceidbruce Posts: 6,197
    edited 2014-06-07 13:34
    Mike G

    I am sure you are correct, however I never got that far with my SPIN programming.

    My goal at this point is to make a working strategy from small bits and pieces, which can later be optimized with methods such as you suggest. I still have not determined the actual data size required for each field element, whereas some of the data fields will only have three characters or less.

    Still trying to figure it out, but thanks for your suggestion. I will take a much deeper look at this, when I get further along.

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2014-06-07 17:27
    This post will be referring to the G-Code command list provided in Post#6 and the G-Code provided in Post #26.

    After closely examining the G-Code file, created by KISSlicer, it was determined that only the following commands were used to create the 3D print.
    G1: Linear Motion at Feed Rate - Example: G1 X90.6 Y13.8 E22.4
    G21: Set Units to Millimeters - Example: G21
    G28: Home - Example: G28
    G90: Set to Absolute Positioning - Example: G90
    M83: Set E codes relative
    M104: Set Extruder Temperature (Fast) - Example: M104 S190
    M106: Set Fan Speed / Set Device Power - Example: M106 S120 Simply M106 in G-Code
    M109: Set Extruder Temperature and Wait - Example: M109 S185
    M107: Fan Off Used in g-Code
    M140: Set heated bed temperature - Example: M140 S55

    So I am assuming, that I can reasonably assume :) that these are the only actual commands that I need to run a 3D printer. However to make slicing and machine setup easier, I suppose I should also include the counterparts to some of the above commands, such as:
    G20: Set Units to Inches - Example: G20
    G91: Set to Relative Positioning - Example: G91
    M82: Set E codes absolute

    Additionally, some commands were used in the G-Code that were not present in the list of Post #6. Considering that I am making a lot of assumptions, I might as well keep assuming, so I assume that the required G-Code commands are determined by the slicer utilized. Additionally, I would imagine that many developers have taken pain staking measures into placing simple machine functions into proper G-Code format, which I also imagine takes up a fair bit of memory. Take for example the following command:
    M112: Emergency Stop - Example: M112

    I can only imagine how much time, effort, and memory it would take to incorporate this command into the code, when a simple WAITPEQ monitoring switch input should be able to provide a machine stop. However an orderly machine stop may be a different issue.

    In the case of a Propeller driven 3D printer, I truly believe the command and coding should be kept to a bare minimum, until it has been determined that there is room for expansion.
  • idbruceidbruce Posts: 6,197
    edited 2014-06-07 18:07
    In addition to my previous post and in an effort to move forward, I would like to state the direction I am heading.

    A previous employer always told me to tackle the known problems first. With this concept rearranged a little in my mind, this is what I know:
    1. Since the controller of Post #1 will only support one extruder, I will only be writing code to support one extruder.
    2. Since I only have a slight amount of experience with one slicer, being the KISSlicer, that is the only slicer I will be attempting to support.
    3. I will only be writing code to support some of the G-Codes utilized by the KISSlicer, which currently consists of the following:
    G1: Linear Motion at Feed Rate - Example: G1 X90.6 Y13.8 E22.4
    G20: Set Units to Inches - Example: G20
    G21: Set Units to Millimeters - Example: G21
    G28: Home - Example: G28
    G90: Set to Absolute Positioning - Example: G90
    G91: Set to Relative Positioning - Example: G91
    M82: Set E codes absolute
    M83: Set E codes relative
    M104: Set Extruder Temperature (Fast) - Example: M104 S190
    M106: Set Fan Speed / Set Device Power - Example: M106 S120 Simply M106 in G-Code
    M109: Set Extruder Temperature and Wait - Example: M109 S185
    M107: Fan Off Used in g-Code
    M140: Set heated bed temperature - Example: M140 S55
    EDIT: Additionally, considering that it slightly affects memory, the build volume area will be limited to 999.99mm X 999.99mm X 999.99mm
    EDIT: As it pertains to the source code of Post #66, Fields P, R, and N, will no longer be supported, because apparently KISSlicer does not support them.
  • neildarlowneildarlow Posts: 4
    edited 2014-06-08 03:31
    I have looked more into the practicalities of using the Propeller for my 3D Printer controller and reached the conclusion that it's a non-starter for the following reasons:

    1) Code space - At only 496 words of available code and data space per cog I would be forced to use an external code memory model. For PropGCC this would be XMMC which leads to...
    2) External code memory model performance hit - I get the impression that employing the PropGCC XMMC memory model hits execution time by a factor of x30 (or thereabouts) for I2C EEPROM. It also requires the use of an instruction cache for each cog taken from Hub memory.
    3) Delays in accessing global Hub resources - It is quite likely that globals in Hub memory would be heavily used and the cog synchronisation delays would impact performance considerably.
    3) More Propellers? - I considered increasing the number of Propellers from 1 to 4 (in order to increase the distributed code space and keep code internal) and link these master-slave fashion using SPI but it increases complexity considerably.
    4) Hardware interface overhead - With the Propeller's lack of on-chip peripherals you need to code hardware interfaces e.g. UART and SPI. This uses valuable resources that the application itself requires.

    The Propeller has a unique architecture which suits smaller, demanding applications. Its lack of a good external memory interface is a severely limiting factor in larger applications which, I think, necessitates the use of multiple Propellers. Given that I already had a dual-PIC solution on the drawing board, which I am sure will be practical, I think I shall pursue that avenue.

    Regards,
    Neil Darlow
  • idbruceidbruce Posts: 6,197
    edited 2014-06-08 07:15
    The following source code and attachments actively reflect a more accurate and detailed solution. Besides the source code shown, the attachment contains all the files necessary for testing and accuracy checking.
    • In the attachment below, CAL_HOLD.TXT is a G-Code file compiled by the KISSlicer and is utilized as a source for the parser.
    • In the attachment below, TRIMMED.TXT is identical to CAL_HOLD.TXT, with the exception that all comments and linefeeds have been removed. This file can be utilized to check the accuracy of the output, as well as checking the assignments of my variables.
    CON
      _CLKMODE        = XTAL1 + PLL16X
      _XINFREQ        = 5_000_000
      'Propeller Memory Card Assignments
      DI              = 0 'IO0 / DI / MOSI / CD Pin
      DO              = 1 'IO1 / DO / MISO Pin
      WP              = 2 'IO2 / WP Pin
      HOLD            = 3 'IO3 / HOLD Pin
      SD_CS           = 4 'CS Pin For MicroSD Card Memory
      FLASH_CS        = 5 'CS Pin For Flash Memory
      SRAM_CS         = 6 'CS Pin For SRAM Memory
      CLK             = 7 'CLK Pin
      'Full Duplex Serial
      RX              = 31   
      TX              = 30  
      COMM_MODE       = 0
      COMM_BAUD_RATE  = 115_200
      'FAT Engine Assignment for unused pins or RTC. Please refer to the
      'FAT16/FAT32 Full File System Driver Documentation for proper usage
      NEG_ONE         = -1
       
    VAR
      'The following variable holds the G-Code file line characters.  It has been
      'established that the G-Code file used as a guide in this code, had a maximum
      'character count of 72.  To be on the safe side, the maximum file line size
      'has been increased to 100. 
      BYTE bChBuffer[100]
      'The following variables are intended to store G-Code into various fields.
      'For more information about these fields, please refer to RepRap G Code
      'Fields at: [URL]http://reprap.org/wiki/G-code[/URL] .  Additionally, these are the
      'only known fields to be supported by the KISSlicer.  Please note that the
      'maximum character count may change in the future, because I am not exactly
      'sure on bFieldElement_8 maximum character count, but support for 7 characters
      'should be sufficient.  The maximum character count should support a build
      'volume area of 999.99mm(X) X 999.99mm(Y) X 999.99mm(Z).  
      BYTE bFieldElement_0[2] 'G Field
      BYTE bFieldElement_1[3] 'M Field
      BYTE bFieldElement_2[1] 'T Field
      BYTE bFieldElement_3[3] 'S Field
      BYTE bFieldElement_4[7] 'X Field - Floating Point
      BYTE bFieldElement_5[7] 'Y Field - Floating Point
      BYTE bFieldElement_6[7] 'Z Field - Floating Point
      BYTE bFieldElement_7[7] 'F Field - Floating Point
      BYTE bFieldElement_8[7] 'E Field - Floating Point
      'The following G-Code commands are currently the only known (by me) commands
      'supported by the KISSlicer, therefore these are the only G-Code commands
      'that I will currently support.
      {
      G1: Controlled Move With E, F, X, Y, And Z Fields.
      G20: Set Measurement Units To Inches With No Fields
      G21: Set Measurement Units To Millimeters With No Fields
      G28: Home Axes With No Fields
      G90: Set Absolute Positioning With No Fields
      G91: Set Relative Positioning With No Fields
      M82: Set E Codes Absolute With No Fields
      M83: Set E Codes Relative With No Fields
      M104: Set Extruder Temperature And No Wait With S Field
      M106: Fan On With No Fields
      M109: Set Extruder Temperature And Wait With S Field
      M107: Fan Off With No Fields
      M140: Set Bed Temperature With S Field
      T0: Select Tool With No Fields 
      }
    OBJ
      SDCard:       "SD-MMC_FATEngine.spin"
      Memory:       "SPI Memory Driver"
      Terminal:     "FullDuplexSerial"
    PUB Main
      'Start FullDuplexSerial
      Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
      WAITCNT(CNT + (1 * CLKFREQ))
      'Start FATEngine
      SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
      'Mount Partition
      SDCard.mountPartition(0)
      'Open G-Code File "CAL_HOLD.TXT" for reading
      SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
      REPEAT
        'Fill bChBuffer with 0s
        BYTEFILL(@bChBuffer, 0, 100)
        'Grab file lines that are not comments or blank
        GetNextValidFileLine
        IF(bChBuffer[0] == 0)
          QUIT
        'Fill in the various field elements
        FillFieldElements
        'Display the various field seperations
        Terminal.Str(STRING("G="))    
        Terminal.Str(@bFieldElement_0)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("M="))
        Terminal.Str(@bFieldElement_1)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("T="))
        Terminal.Str(@bFieldElement_2)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("S="))
        Terminal.Str(@bFieldElement_3)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("X="))
        Terminal.Str(@bFieldElement_4)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("Y="))
        Terminal.Str(@bFieldElement_5)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("Z="))
        Terminal.Str(@bFieldElement_6)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("F="))
        Terminal.Str(@bFieldElement_7)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("E="))
        Terminal.Str(@bFieldElement_8)
        Terminal.Tx(32) {Space}
        'Seperate G-Code field values line by line
        Terminal.Tx(13) {Carriage Return}
      'Unmount Partition
      SDCard.unmountPartition
      'Stop FATEngine
      SDCard.fatEngineStop
      WAITCNT(CNT + (1 * CLKFREQ))
      'Stop FullDuplexSerial  
      Terminal.Stop
    PUB GetNextValidFileLine
      REPEAT
        'Grab a file line as a string  
        SDCard.readString(@bChBuffer, 99)
        'Keep repeating while the following conditions are met,
        'because we don't want or need these lines beginning with
        'these characters.
        IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
          QUIT
    PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
      'Empty the field elements of any possible data
      BYTEFILL(@bFieldElement_0, 0, 2)
      BYTEFILL(@bFieldElement_1, 0, 3)
      BYTEFILL(@bFieldElement_2, 0, 1)
      BYTEFILL(@bFieldElement_3, 0, 3)
      BYTEFILL(@bFieldElement_4, 0, 7)
      BYTEFILL(@bFieldElement_5, 0, 7)
      BYTEFILL(@bFieldElement_6, 0, 7)
      BYTEFILL(@bFieldElement_7, 0, 7)
      BYTEFILL(@bFieldElement_8, 0, 7)
      Index := 0
      ValueIndex := 0
      'Parse file line into seperate field values.  Various fields will remain empty
      REPEAT
        Char := bChBuffer[Index]
          'Exclude these characters from becoming part of are field entries
          IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
            QUIT
          ELSEIF(Char == 32 {Space})
            Index++
            ValueIndex := 0
            NEXT
          ELSEIF(Char == 71 {G Character})
            'Set current field element
            Field_Element := 0
            Index++
            NEXT
          ELSEIF(Char == 77 {M Character})
            'Set current field element
            Field_Element := 1
            Index++
            NEXT
          ELSEIF(Char == 84 {T Character})
            'Set current field element
            Field_Element := 2
            Index++
            NEXT
          ELSEIF(Char == 83 {S Character})
            'Set current field element
            Field_Element := 3
            Index++
            NEXT
          ELSEIF(Char == 88 {X Character})
            'Set current field element
            Field_Element := 4
            Index++
            NEXT
          ELSEIF(Char == 89 {Y Character})
            'Set current field element
            Field_Element := 5
            Index++
            NEXT
          ELSEIF(Char == 90 {Z Character})
            'Set current field element
            Field_Element := 6
            Index++
            NEXT
          ELSEIF(Char == 70 {F Character})
            'Set current field element
            Field_Element := 7
            Index++
            NEXT
          ELSEIF(Char == 69 {E Character})
            'Set current field element
            Field_Element := 8
            Index++
            NEXT
          ELSE
            IF(Field_Element == 0)
              'G Field element character
              bFieldElement_0[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 1)
              'M Field element character
              bFieldElement_1[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 2)
              'T Field element character
              bFieldElement_2[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 3)
              'S Field element character
              bFieldElement_3[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 4)
              'X Field element character
              bFieldElement_4[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 5)
              'Y Field element character
              bFieldElement_5[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 6)
              'Z Field element character
              bFieldElement_6[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 7)
              'F Field element character
              bFieldElement_7[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 8)
              'E Field element character
              bFieldElement_8[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
  • idbruceidbruce Posts: 6,197
    edited 2014-06-08 12:19
    For Those That May Be Interested In 3D Printing

    I found a nice web page today http://www.repetier.com/documentation/repetier-firmware/rf-installation/
  • idbruceidbruce Posts: 6,197
    edited 2014-06-09 00:57
    This post, source code, and attachment is nearly identical to Post #72 and I apologize for the redundancy, however the comments provided in the source code of Post #72 were a little misleading. In an effort to provide a more clear understanding, I am now providing new source code, with rewritten source code comments.
    CON
      _CLKMODE        = XTAL1 + PLL16X
      _XINFREQ        = 5_000_000
      'Propeller Memory Card Assignments
      DI              = 0 'IO0 / DI / MOSI / CD Pin
      DO              = 1 'IO1 / DO / MISO Pin
      WP              = 2 'IO2 / WP Pin
      HOLD            = 3 'IO3 / HOLD Pin
      SD_CS           = 4 'CS Pin For MicroSD Card Memory
      FLASH_CS        = 5 'CS Pin For Flash Memory
      SRAM_CS         = 6 'CS Pin For SRAM Memory
      CLK             = 7 'CLK Pin
      'Full Duplex Serial
      RX              = 31   
      TX              = 30  
      COMM_MODE       = 0
      COMM_BAUD_RATE  = 115_200
      'FAT Engine Assignment for unused pins or RTC. Please refer to the
      'FAT16/FAT32 Full File System Driver Documentation for proper usage
      NEG_ONE         = -1
       
    VAR
      'The following variable holds the G-Code file line characters.  It has been
      'established that the G-Code file used as a guide in this code, had a maximum
      'file line character count of 72.  To be on the safe side, the maximum file
      'line size has been increased to 100. 
      BYTE bChBuffer[100]
      'The following variables are intended to store G-Code into various fields.
      'For more information about these fields, please refer to RepRap G Code
      'Fields at: [URL]http://reprap.org/wiki/G-code[/URL] .  Additionally, these are the
      'only known fields to be supported by the KISSlicer.  Please note that the
      'maximum character count may change in the future, because I am not exactly
      'sure on bFieldElement_8 maximum character count, but support for 7 characters
      'should be sufficient.  The maximum character count should support a build
      'volume area of 999.99mm(X) X 999.99mm(Y) X 999.99mm(Z).  
      BYTE bFieldElement_0[2] 'G Field
      BYTE bFieldElement_1[3] 'M Field
      BYTE bFieldElement_2[1] 'T Field
      BYTE bFieldElement_3[3] 'S Field
      BYTE bFieldElement_4[7] 'X Field - Floating Point
      BYTE bFieldElement_5[7] 'Y Field - Floating Point
      BYTE bFieldElement_6[7] 'Z Field - Floating Point
      BYTE bFieldElement_7[7] 'F Field - Floating Point
      BYTE bFieldElement_8[7] 'E Field - Floating Point
      'The following G-Code commands are currently the only known (by me) commands
      'supported by the KISSlicer, therefore these are the only G-Code commands
      'that I will currently support.
      {
            G1: Controlled Move
                  G Field - bFieldElement_0, X Field - bFieldElement_4,
                  Y Field - bFieldElement_5, Z Field - bFieldElement_6,
                  F Field - bFieldElement_7, and E Field - bFieldElement_8
                  filled during line parse 
      
            G20: Set Measurement Units To Inches
                  G Field - bFieldElement_0 filled during line parse
            
            G21: Set Measurement Units To Millimeters
                  G Field - bFieldElement_0 filled during line parse
            
            G28: Home Axes
                  G Field - bFieldElement_0 filled during line parse
            
            G90: Set Absolute Positioning
                  G Field - bFieldElement_0 filled during line parse
            
            G91: Set Relative Positioning
                  G Field - bFieldElement_0 filled during line parse
            
            M82: Set E Codes Absolute
                  M Field - bFieldElement_1 filled during line parse
            
            M83: Set E Codes Relative
                  M Field - bFieldElement_1 filled during line parse
            
            M104: Set Extruder Temperature With No Wait
                  M Field - bFieldElement_1 and S Field - bFieldElement_3
                  filled during line parse
      
            M106: Fan On
                  M Field - bFieldElement_1 filled during line parse
      
            M109: Set Extruder Temperature And Wait
                  M Field - bFieldElement_1 and S Field - bFieldElement_3
                  filled during line parse
      
            M107: Fan Off
                  M Field - bFieldElement_1 filled during line parse
      
            M140: Set Bed Temperature
                  M Field - bFieldElement_1 and S Field - bFieldElement_3
                  filled during line parse
      
            T0: Select Tool With No Fields
                  T Field - bFieldElement_2 filled during line parse   
      }
    OBJ
      SDCard:       "SD-MMC_FATEngine.spin"
      Memory:       "SPI Memory Driver"
      Terminal:     "FullDuplexSerial"
    PUB Main
      'Start FullDuplexSerial
      Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
      WAITCNT(CNT + (1 * CLKFREQ))
      'Start FATEngine
      SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
      'Mount Partition
      SDCard.mountPartition(0)
      'Open G-Code File "CAL_HOLD.TXT" for reading
      SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
      REPEAT
        'Fill bChBuffer with 0s
        BYTEFILL(@bChBuffer, 0, 100)
        'Grab file lines that are not comments or blank
        GetNextValidFileLine
        IF(bChBuffer[0] == 0)
          QUIT
        'Fill in the various field elements
        FillFieldElements
        'Display the various field seperations
        Terminal.Str(STRING("G="))    
        Terminal.Str(@bFieldElement_0)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("M="))
        Terminal.Str(@bFieldElement_1)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("T="))
        Terminal.Str(@bFieldElement_2)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("S="))
        Terminal.Str(@bFieldElement_3)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("X="))
        Terminal.Str(@bFieldElement_4)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("Y="))
        Terminal.Str(@bFieldElement_5)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("Z="))
        Terminal.Str(@bFieldElement_6)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("F="))
        Terminal.Str(@bFieldElement_7)
        Terminal.Tx(32) {Space}
        Terminal.Str(STRING("E="))
        Terminal.Str(@bFieldElement_8)
        Terminal.Tx(32) {Space}
        'Seperate G-Code field values line by line
        Terminal.Tx(13) {Carriage Return}
      'Unmount Partition
      SDCard.unmountPartition
      'Stop FATEngine
      SDCard.fatEngineStop
      WAITCNT(CNT + (1 * CLKFREQ))
      'Stop FullDuplexSerial  
      Terminal.Stop
    PUB GetNextValidFileLine
      REPEAT
        'Grab a file line as a string  
        SDCard.readString(@bChBuffer, 99)
        'Keep repeating while the following conditions are met,
        'because we don't want or need these lines beginning with
        'these characters.
        IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
          QUIT
    PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
      'Empty the field elements of any possible data
      BYTEFILL(@bFieldElement_0, 0, 2)
      BYTEFILL(@bFieldElement_1, 0, 3)
      BYTEFILL(@bFieldElement_2, 0, 1)
      BYTEFILL(@bFieldElement_3, 0, 3)
      BYTEFILL(@bFieldElement_4, 0, 7)
      BYTEFILL(@bFieldElement_5, 0, 7)
      BYTEFILL(@bFieldElement_6, 0, 7)
      BYTEFILL(@bFieldElement_7, 0, 7)
      BYTEFILL(@bFieldElement_8, 0, 7)
      Index := 0
      ValueIndex := 0
      'Parse file line into seperate field values.  Various fields will remain empty
      REPEAT
        Char := bChBuffer[Index]
          'Exclude these characters from becoming part of are field entries
          IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
            QUIT
          ELSEIF(Char == 32 {Space})
            Index++
            ValueIndex := 0
            NEXT
          ELSEIF(Char == 71 {G Character})
            'Set current field element
            Field_Element := 0
            Index++
            NEXT
          ELSEIF(Char == 77 {M Character})
            'Set current field element
            Field_Element := 1
            Index++
            NEXT
          ELSEIF(Char == 84 {T Character})
            'Set current field element
            Field_Element := 2
            Index++
            NEXT
          ELSEIF(Char == 83 {S Character})
            'Set current field element
            Field_Element := 3
            Index++
            NEXT
          ELSEIF(Char == 88 {X Character})
            'Set current field element
            Field_Element := 4
            Index++
            NEXT
          ELSEIF(Char == 89 {Y Character})
            'Set current field element
            Field_Element := 5
            Index++
            NEXT
          ELSEIF(Char == 90 {Z Character})
            'Set current field element
            Field_Element := 6
            Index++
            NEXT
          ELSEIF(Char == 70 {F Character})
            'Set current field element
            Field_Element := 7
            Index++
            NEXT
          ELSEIF(Char == 69 {E Character})
            'Set current field element
            Field_Element := 8
            Index++
            NEXT
          ELSE
            IF(Field_Element == 0)
              'G Field element character
              bFieldElement_0[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 1)
              'M Field element character
              bFieldElement_1[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 2)
              'T Field element character
              bFieldElement_2[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 3)
              'S Field element character
              bFieldElement_3[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 4)
              'X Field element character
              bFieldElement_4[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 5)
              'Y Field element character
              bFieldElement_5[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 6)
              'Z Field element character
              bFieldElement_6[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 7)
              'F Field element character
              bFieldElement_7[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
            ELSEIF(Field_Element == 8)
              'E Field element character
              bFieldElement_8[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
  • Mike GMike G Posts: 2,702
    edited 2014-06-09 14:56
    My goal at this point is to make a working strategy from small bits and pieces, which can later be optimized with methods such as you suggest. I still have not determined the actual data size required for each field element, whereas some of the data fields will only have three characters or less.

    Implementing a structure that defines data relationships reduces the amount of conditional statements in the code base. Besides, it is good design strategy to define this structures upfront before the code base becomes too complex.
    CON
    
      _clkmode = xtal1 + pll16x     
      _xinfreq = 5_000_000
      
      #0, G_FIELD, M_FIELD, T_FIELD, S_FIELD, X_FIELD,{
    }     Y_FIELD, Z_FIELD, F_FIELD, E_FIELD  
         
    
      G_LEN = 2
      M_LEN = 3
      T_LEN = 1
      S_LEN = 3
      X_LEN = 7
      Y_LEN = 7
      Z_LEN = 7
      F_LEN = 7
      E_LEN = 7
    
      NUMBER_OF_FIELDS = 9
    
    DAT  
    gf  byte   $0[G_LEN]
    mf  byte   $0[M_LEN]
    tf  byte   $0[T_LEN]
    sf  byte   $0[S_LEN]
    xf  byte   $0[X_LEN]
    yf  byte   $0[Y_LEN]
    zf  byte   $0[Z_LEN]
    ff  byte   $0[F_LEN]
    ef  byte   $0[E_LEN]
     
    ikey    byte   "G", "M", "T", "S", "X", "Y", "Z", "F", "E"
    ivalue  long   @gf, @mf, @tf, @sf, @xf, @yf, @zf, @ff, @ef
    ilen    byte   2,   3,    1,   3,   7,   7,   7,   7,   7
     
    
    OBJ
      pst             : "Parallax Serial Terminal"
    
    PUB Main | i, idx
      pst.Start(115_200)     
      pause(500)
    
      pst.str(string("Complex Data Structure", 13))
    
      'Get the "S" field and fill the field with data
      idx := GetTableIndex("S")
    
      'file S with ASCII bytes 0, 1, and 2
      repeat i from 0 to (ilen[idx] - 1)
        byte[@@ivalue[idx] + i] := i + "0"
    
      'Zero terminate
      byte[@@ivalue[idx] + idx] := 0
    
      'View results
      pst.str(@@ivalue[idx])
    
    
    PRI GetTableIndex(key) | i
      repeat i from 0 to NUMBER_OF_FIELDS - 1
        if ikey[i] == key
          return i
      return -1
      
    
    PRI pause(Duration)  
      waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
      return
    
  • idbruceidbruce Posts: 6,197
    edited 2014-06-09 15:22
    Mike

    That looks a whole lot better than I am capable of at this point in time :) In fact, it looks real good. As mentioned earlier, I still consider myself a beginner in SPIN programming, and my skills are no where near your skills.

    Just struggling my way through it, and trying to get a working model.

    Bruce
  • rosco_pcrosco_pc Posts: 464
    edited 2014-06-09 18:14
    idbruce wrote: »
    Mike

    That looks a whole lot better than I am capable of at this point in time :) In fact, it looks real good. As mentioned earlier, I still consider myself a beginner in SPIN programming, and my skills are no where near your skills.

    Just struggling my way through it, and trying to get a working model.

    Bruce

    You might want to take a look here: https://github.com/rosco-pc/propmaker

    It has a fully working g-code parser, but no implementation of the actual commands. I actually lost interest after awhile
  • idbruceidbruce Posts: 6,197
    edited 2014-06-09 18:38
    rosco_pc

    Looks interesting..... However, I am going to attempt it from scratch. I want to attempt the machine control with my own interpretation of the G-Codes and applying my own strategy for a positioning queue. However, I am more than willing to borrow the necessary algorithms :)

    By looking at the included objects of my example, I suppose "from scratch" would not be an accurate definition :)

    But thanks for sharing, because someone may want to use that information to their benefit.

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2014-06-10 02:30
    Okay.... I must admit that it was kind of cool to parse the lines and see all of the fields broken down to their values, just zipping through the terminal, however for testing and coding purposes, the huge G-Code file (actually quite small in comparison to large objects) made it quite cumbersome. To speed up the testing and development process, I have decided to switch over to a much smaller file, which includes all test conditions.

    The switch to a smaller file is only temporary and I will let you know when I switch back to the original file.

    For those of you who might be following along or developing your own version, I am including a copy of that minimalistic file.

    EDIT: This file made an error. See the next post.
  • idbruceidbruce Posts: 6,197
    edited 2014-06-10 03:03
    In the attached file above, I went out of the array bounds by one character for bFieldElement_8.

    To temporarily resolve this issue, I did the following:
    'BYTE bFieldElement_8[7] 'E Field - Floating Point
    BYTE bFieldElement_8[8] 'E Field - Floating Point

    'BYTEFILL(@bFieldElement_8, 0, 7)
    BYTEFILL(@bFieldElement_8, 0, 8)
  • Mike GMike G Posts: 2,702
    edited 2014-06-10 04:31
    Slight update for those reading along. Replaced the hard coded field properties (ilen) with CONstants.
    ikey    byte   "G", "M", "T", "S", "X", "Y", "Z", "F", "E"
    ilen    byte   G_LEN,M_LEN,T_LEN,S_LEN,X_LEN,Y_LEN,Z_LEN,F_LEN,E_LEN
    ivalue  long   @gf, @mf, @tf, @sf, @xf, @yf, @zf, @ff, @ef
    
  • idbruceidbruce Posts: 6,197
    edited 2014-06-10 06:07
    Mike

    Since you are participating, let me throw my strategy at you for some feedback.

    Cog 0:
    Although I am not 100% positive, because I have not reached the math yet, I believe one cog can parse and compute far faster than the machine can possibly move. Therefore I want to read one line and compute and write the results to a predetermined location in SRAM, for a maximum of 10 lines to start. Monitor SRAM locations for deletion, and upon deletion, add another set of computations for a line.

    Cog 1:
    Read the computations made from Cog 0 per active line at the predetermined locations in SRAM. Initiate proper machine procedures, for simple commands such as M106, simply perform the task, and delete SRAM contents for that line procedure. For complex procedures such as G1, initiate several new cogs 2, 3, 4, & 5 to perform movement, during which time, Cog 1 could have read the appropriate SRAM location, deleted the contents, and be waiting with the computations when Cogs 2, 3, 4, & 5 return.

    Cog 2:
    X motor movement

    Cog 3:
    Y motor movement

    Cog 4:
    Z motor movement

    Cog 5:
    E motor movement

    Of course Cogs 2, 3, 4, & 5 are arbitrary, since there will be times when it will not be necessary for all these motors to move, so next avail cog.

    As far as I can foresee, the only downside would be a small waiting period to initially fill the SRAM in Cog 1, and a small waiting time in Cog 1, when simple tasks are performed, because there will have to be a following read to SRAM for new movement computations or machine procedure. However, since there are several cogs still available, the read of Cog 1 could be passed to another cog, while Cog 1 performs another read.

    I know there are those that say the motor movement can be performed in one cog, but right now, this is my strategy.
  • Mike GMike G Posts: 2,702
    edited 2014-06-10 11:01
    My knee jerk reaction is to implement an observer pattern to manage caching. I would probably do something similar, if not the same, to manage X, Y, Z, and E movements.
  • idbruceidbruce Posts: 6,197
    edited 2014-06-10 11:50
    I am not convinced this is the way to go, but here is a link to Bresenham's algorithm implemented in C for a 3D printer. It appears that a port would be real simple.

    http://gist.github.com/nitsky/6146728

    EDIT: Here is another article and source code for 3D printing

    http://ravehgonen.wordpress.com/2013/02/19/stereolithography-3d-printing-algorithms-and-thoughts/
  • MagIO2MagIO2 Posts: 2,243
    edited 2014-06-10 13:47
    I give up posting here in the forum ...

    Bresnham is exactly what is in the StepperTest.zip
  • idbruceidbruce Posts: 6,197
    edited 2014-06-10 13:59
    Okay so the values can be seperated and you say NOTHING SPECIAL

    I totally agree, but it was a first step. Now comes the hard part, but before we can work on the hard part, a strategy or several strategies must be outlined. One of these rough strategies was outlined in Post #82, however this is very far from a complete solution.

    Before I go any further with this discussion, let me just state for the record that I am no mathematical genius. In fact, math is my weakest link, but I have managed to struggle through life so far with this handicap.

    Many 3D print strategies rely on a constant speed for each axis, however I see this as an easy way out, just to get operational software and printers. Several years ago, when I received my first Gecko G251 stepper drives, I was using traditional SPIN to run my steppers, but the speed of these motors was way to slow for my satisfaction. I then started a thread about using counters to run these stepper drives. Several people participated in the discussion and I am thankful to all of them, however between MagIO2 and JonnyMac, a snippet was produced which really made my stepper motors "ZING", from that point on, I have periodically experimented with counter based stepper drivers. The stepper drivers have developed to this stage:
    1. Start the motor at a pulse rate which is capable of overcoming the moment of inertia.
    2. Keep decreasing the time between pulses until we lose necessary torque (MAXIMUM SPEED) for axis movement. - RAMP UP
    3. Maintain maximum speed until ramp down becomes necessary - RUN MAINTAINED SPEED
    4. Keep increasing the time between pulses until we reach the stopping point - RAMP DOWN
    By using the longest travel of an axis, like in the Bresenham algorithm, I believe profiles could be determined for all axes and run the steppers with ramping by use of the counters. As mentioned in Post #82 of this thread and in various other threads, all steppers would run in seperate cogs, with seperate counters.

    I suppose the main question is, can a straight line be computed by time? I think it can, but then comes the issue of the feedrate or F field, so I am really not sure how it all plays out. To be honest, I really do not believe that I have a firm grasp on F just yet.
  • idbruceidbruce Posts: 6,197
    edited 2014-06-10 14:06
    MagIO2

    Please don't be discouraged.... I am sorry I still have not responded..... I am just trying to figure all this out.
  • Igor_RastIgor_Rast Posts: 357
    edited 2015-01-23 09:48
    Attachment not found.

    Up for a new challange ?

    Im currently also building an firepick delta , pick&place /3d print/ solderpaste dispencer/ chocolate printer ( hihi) . machine , it could do allot more
    anyway , we have been using an atmega1284p running with an 20mhx crystal , marlin driver loaded .
    but its just not handeling the inverse kinematics good enought to get the precision needed

    currently there there is an driver change going on , to go from marlin to repeater firmware for the motor controller board. still long way from working
    but I always had the idea our little friend (the propeller ) could do a way better job .

    Your lined out approuche below is kinda what I think we also need.
    But im also starting from scratch, just to be able to fit it exactly on our needs and make a custom board for it . looking if it may be an good alternative to our current , or our future probable (ARM ) design

    its a opensource project , wich was best known tru hackaday . there is a page there with allot of info on it
    also github pages with all the till now released files and sources , google group where the chatting happens ( its an active community) . so its going pretty good with progress


    I have propeller chips lying around to do actual testing and hacking ,
    drivers used are polou DRV8825 wich are set and shoudl stay at 1/16 microsteps .
    the inverse kinematics is what killing the slow 8 bit processor
    think the splitting up of the math in different cogs , and letting couters run them so we can push them pretty hard somehow should made a great driver

    Iill take some time reading the old post , but would love some links code snippets to get some steppers moving etc . all help appriciated

    Igor


    https://github.com/firepick-delta

    https://groups.google.com/forum/?hl=en#!forum/firepick

    http://hackaday.io/project/963-300-pick-and-place-3d-printer


    Image Links
    http://imgur.com/sJytsPP
    http://imgur.com/XeE8o4A
    http://imgur.com/WzkbjCv

    idbruce wrote: »
    Mike

    Since you are participating, let me throw my strategy at you for some feedback.

    Cog 0:
    Although I am not 100% positive, because I have not reached the math yet, I believe one cog can parse and compute far faster than the machine can possibly move. Therefore I want to read one line and compute and write the results to a predetermined location in SRAM, for a maximum of 10 lines to start. Monitor SRAM locations for deletion, and upon deletion, add another set of computations for a line.

    Cog 1:
    Read the computations made from Cog 0 per active line at the predetermined locations in SRAM. Initiate proper machine procedures, for simple commands such as M106, simply perform the task, and delete SRAM contents for that line procedure. For complex procedures such as G1, initiate several new cogs 2, 3, 4, & 5 to perform movement, during which time, Cog 1 could have read the appropriate SRAM location, deleted the contents, and be waiting with the computations when Cogs 2, 3, 4, & 5 return.

    Cog 2:
    X motor movement

    Cog 3:
    Y motor movement

    Cog 4:
    Z motor movement

    Cog 5:
    E motor movement

    Of course Cogs 2, 3, 4, & 5 are arbitrary, since there will be times when it will not be necessary for all these motors to move, so next avail cog.

    As far as I can foresee, the only downside would be a small waiting period to initially fill the SRAM in Cog 1, and a small waiting time in Cog 1, when simple tasks are performed, because there will have to be a following read to SRAM for new movement computations or machine procedure. However, since there are several cogs still available, the read of Cog 1 could be passed to another cog, while Cog 1 performs another read.

    I know there are those that say the motor movement can be performed in one cog, but right now, this is my strategy.
  • idbruceidbruce Posts: 6,197
    edited 2015-01-23 11:00
    Igor

    I have just recently started to toy around with the CNC controller discussed in this thread: http://forums.parallax.com/showthread.php/155404-Input-Needed-Combining-Propeller-Proto-Board-Prop-DIP-40-and-ADC-for-3D-Printer. In additon to testing various aspects of the controller, I have also been testing X and Y axes. Both X and Y are driven by Gecko G251 stepper drives, which have 10 microsteps. Although I do no not have any coordinated movement at this time, the code below contains all the constants for the controller board pins, as well as some miscellaneous code, which includes stepper movement code. That code is currently setup for the G251 drives, but it should be able to drive any drivers that have step, direction, and disable inputs, providing the correct value for the constants are given. The main method for driving the steppers is called PulseTheStepPin. More information about this method can be found at this location: http://forums.parallax.com/showthread.php/132373-PulseTheStepPin-revisited-Questions-answers-problems-solutions

    There is also other information available elsewhere, which can most likely be found with a seach of the function name.

    Hope this helps
    CON
    
      _CLKMODE        = XTAL1 + PLL16X
      _XINFREQ        = 5_000_000
    
      'Z Axis Pin Assignments
      Z_AXIS_DISABLE    = 0
      Z_AXIS_STEP       = 1
      Z_AXIS_DIRECTION  = 2
    
      'ESTOP And Limit Switches Pin Assignments
      ESTOP_AND_LIMIT   = 3
    
      'Serial LCD Display Pin Assignment
      SERIAL_LCD        = 4
    
      'Homing Switch Pin Assignments
      X_HOMING          = 5
      Y_HOMING          = 6
      Z_HOMING          = 7
    
      'Propeller Memory Card Pin Assignments
      DI                = 8 'IO0 / DI / MOSI / CD Pin
      DO                = 9 'IO1 / DO / MISO Pin
      WP                = 10 'IO2 / WP Pin
      HOLD              = 11 'IO3 / HOLD Pin
      SD_CS             = 12 'CS Pin For MicroSD Card Memory
      FLASH_CS          = 13 'CS Pin For Flash Memory
      SRAM_CS           = 14 'CS Pin For SRAM Memory
      CLK               = 15 'CLK Pin
    
      'Daughterboard Pin Assignments
      PLATFORM_HEATER   = 16
      EXTRUDER_HEATER   = 17
      EXTRUDER_FAN      = 18
    
      'E Axis Pin Assignments  
      E_AXIS_DIRECTION  = 19
      E_AXIS_STEP       = 20
      E_AXIS_DISABLE    = 21
    
      'X Axis Pin Assignments  
      X_AXIS_DIRECTION  = 22
      X_AXIS_STEP       = 23
      X_AXIS_DISABLE    = 24
    
      'Additional X Axis Constants
      X_AXIS_CYCLE_OFFSET           = 15_000
      X_AXIS_MIN_SPEED              = 4_000 '15_000
      X_AXIS_MAX_SPEED              = 6_000 '20_000
      X_AXIS_SPEED_RANGE            = X_AXIS_MAX_SPEED - X_AXIS_MIN_SPEED
      X_AXIS_RAMPING_RANGE          = X_AXIS_SPEED_RANGE * 2
      X_AXIS_RAMP_ADJUSTER          = 4 '20
    
      'Y Axis Pin Assignments
      Y_AXIS_DISABLE    = 25
      Y_AXIS_STEP       = 26  
      Y_AXIS_DIRECTION  = 27
    
      'Additional Y Axis Constants
      Y_AXIS_CYCLE_OFFSET           = 18_000
      Y_AXIS_MIN_SPEED              = 3_000 '15_000
      Y_AXIS_MAX_SPEED              = 4_000 '20_000
      Y_AXIS_SPEED_RANGE            = Y_AXIS_MAX_SPEED - Y_AXIS_MIN_SPEED
      Y_AXIS_RAMPING_RANGE          = Y_AXIS_SPEED_RANGE * 2
      Y_AXIS_RAMP_ADJUSTER          = 4 '20  
    
      'Full Duplex Serial
      RX                = 31   
      TX                = 30  
      COMM_MODE         = 0
      COMM_BAUD_RATE    = 115_200
    
      'FAT Engine Assignment for unused pins or RTC. Please refer to the
      'FAT16/FAT32 Full File System Driver Documentation for proper usage
      NEG_ONE         = -1
    
        'Clock Frequency Equation
      CLK_FREQ                      = ((_CLKMODE - XTAL1) >> 6) * _XINFREQ
    
      'This is the setting for the minimal step pulse width.
      HIGH_PULSE_WIDTH              = CLK_FREQ / 1_000_000 'CLK_FREQ / 500_000
      MIN_EXECUTION_TIME            = CLK_FREQ / 21_500
       
    VAR
    
      'The following variable holds the G-Code file line characters.  It has been
      'established that the G-Code file used as a guide in this code, had a maximum
      'file line character count of 72.  To be on the safe side, the maximum file
      'line size has been increased to 100. 
      BYTE bChBuffer[100]
    
      'The following variables are intended to store G-Code into various fields.
      'For more information about these fields, please refer to RepRap G Code
      'Fields at: http://reprap.org/wiki/G-code .  Additionally, these are the
      'only known fields to be supported by the KISSlicer.  Please note that the
      'maximum character count may change in the future, because I am not exactly
      'sure on bFieldElement_8 maximum character count, but support for 7 characters
      'should be sufficient.  The maximum character count should support a build
      'volume area of 999.99mm(X) X 999.99mm(Y) X 999.99mm(Z).  
      BYTE bFieldElement_0[2] 'G Field
      BYTE bFieldElement_1[3] 'M Field
      BYTE bFieldElement_2[1] 'T Field
      BYTE bFieldElement_3[3] 'S Field
      BYTE bFieldElement_4[7] 'X Field - Floating Point
      BYTE bFieldElement_5[7] 'Y Field - Floating Point
      BYTE bFieldElement_6[7] 'Z Field - Floating Point
      BYTE bFieldElement_7[7] 'F Field - Floating Point
      'BYTE bFieldElement_8[7] 'E Field - Floating Point Temporary comment
      BYTE bFieldElement_8[8] 'E Field - Floating Point
    
      BYTE bValidFields[9]
    
      'The following G-Code commands are currently the only known (by me) commands
      'supported by the KISSlicer, therefore these are the only G-Code commands
      'that I will currently support.
      {
            G1: Controlled Move
                  G Field - bFieldElement_0, X Field - bFieldElement_4,
                  Y Field - bFieldElement_5, Z Field - bFieldElement_6,
                  F Field - bFieldElement_7, and E Field - bFieldElement_8
                  filled during line parse 
      
            G20: Set Measurement Units To Inches
                  G Field - bFieldElement_0 filled during line parse
            
            G21: Set Measurement Units To Millimeters
                  G Field - bFieldElement_0 filled during line parse
            
            G28: Home Axes
                  G Field - bFieldElement_0 filled during line parse
            
            G90: Set Absolute Positioning
                  G Field - bFieldElement_0 filled during line parse
            
            G91: Set Relative Positioning
                  G Field - bFieldElement_0 filled during line parse
            
            M82: Set E Codes Absolute
                  M Field - bFieldElement_1 filled during line parse
            
            M83: Set E Codes Relative
                  M Field - bFieldElement_1 filled during line parse
            
            M104: Set Extruder Temperature With No Wait
                  M Field - bFieldElement_1 and S Field - bFieldElement_3
                  filled during line parse
      
            M106: Fan On
                  M Field - bFieldElement_1 filled during line parse
      
            M109: Set Extruder Temperature And Wait
                  M Field - bFieldElement_1 and S Field - bFieldElement_3
                  filled during line parse
      
            M107: Fan Off
                  M Field - bFieldElement_1 filled during line parse
      
            M140: Set Bed Temperature
                  M Field - bFieldElement_1 and S Field - bFieldElement_3
                  filled during line parse
      
            T0: Select Tool With No Fields
                  T Field - bFieldElement_2 filled during line parse   
      }
    
    OBJ
    
      SDCard:       "SD-MMC_FATEngine.spin"
      Memory:       "SPI Memory Driver"
      Terminal:     "FullDuplexSerial"
      Expander:     "SimpleMCP23008"
    
    PUB Main
    
      WAITCNT(CLKFREQ * 1 + CNT)
    
      DIRA[X_AXIS_DIRECTION]~~
      DIRA[X_AXIS_STEP]~~
      DIRA[X_AXIS_DISABLE]~~
    
      DIRA[Y_AXIS_DIRECTION]~~
      DIRA[Y_AXIS_STEP]~~
      DIRA[Y_AXIS_DISABLE]~~
    
    '  TestXandY
    '  Parse
      Test_MCP23008
    
    PUB Test_MCP23008 | Value
    
      'GP0 Menu Button
      'GP1 Select Button
      'GP2 Scroll Button
      'GP3 Pause Button
      'GP4 Back Button
      'GP5 Exit Button
      'GP6 Yellow LED
      'GP7 Red LED
    
      Expander.Initialize
      
      Expander.Configure_IODIR_Register(%00111111)  'Configure the IODIR register to 2 outputs and 6 inputs
      Expander.Configure_GPIO_Register(%10000000) '0 is low, GP7, OUTPUT - RED LED
    '  Expander.Configure_GPIO_Register(%01000000)  'Write to the GPIO register, which sets high or low .  0 is low, GP6, OUTPUT - YELLOW LED
    
      REPEAT
    
        Value := Expander.GetPinStates
    
          IF Value == %10010000
    
            Expander.Configure_GPIO_Register(%11000000)
    
          ELSE
    
            Expander.Configure_GPIO_Register(%10000000) '0 is low, GP7, OUTPUT - RED LED
    
    PUB TestXandY
    
      OUTA[X_AXIS_DIRECTION]~~
      PulseTheStepPin(15000, X_AXIS_STEP, X_AXIS_RAMPING_RANGE, X_AXIS_SPEED_RANGE, X_AXIS_CYCLE_OFFSET, X_AXIS_RAMP_ADJUSTER)
    
      WAITCNT(CLKFREQ / 8 + CNT)
      
      OUTA[X_AXIS_DIRECTION]~
      PulseTheStepPin(15000, X_AXIS_STEP, X_AXIS_RAMPING_RANGE, X_AXIS_SPEED_RANGE, X_AXIS_CYCLE_OFFSET, X_AXIS_RAMP_ADJUSTER)
    
      OUTA[Y_AXIS_DIRECTION]~~
      PulseTheStepPin(16000, Y_AXIS_STEP, Y_AXIS_RAMPING_RANGE, Y_AXIS_SPEED_RANGE, Y_AXIS_CYCLE_OFFSET, Y_AXIS_RAMP_ADJUSTER)
    
      WAITCNT(CLKFREQ / 8 + CNT)
      
      OUTA[Y_AXIS_DIRECTION]~
      PulseTheStepPin(16000, Y_AXIS_STEP, Y_AXIS_RAMPING_RANGE, Y_AXIS_SPEED_RANGE, Y_AXIS_CYCLE_OFFSET, Y_AXIS_RAMP_ADJUSTER)   
      
    
    PUB PulseTheStepPin(TotalSteps, StepPin, RampingRange, SpeedRange, CycleOffset, RampSpeedAdjuster) | RampingSteps, RunningSteps, CompensationSteps, Counter
    
      IF TotalSteps > RampingRange
      
        RampingSteps := SpeedRange
        RunningSteps := TotalSteps - RampingRange
        
      ELSE
        RampingSteps := TotalSteps / 2
        RunningSteps := TotalSteps // 2    
      
      CTRA[30..26] := %00100
      CTRA[5..0] := StepPin
      FRQA := 1
      DIRA[StepPin]~~
    
      Counter := CNT
      CompensationSteps := 0
      
      REPEAT RampingSteps
      
        PHSA := -HIGH_PULSE_WIDTH
    
        IF CycleOffset => MIN_EXECUTION_TIME
        
          WAITCNT(Counter += CycleOffset -= RampSpeedAdjuster)
    
        ELSE
    
          WAITCNT(Counter += CycleOffset)
          CompensationSteps++  
        
      REPEAT RunningSteps += CompensationSteps
       
        PHSA := -HIGH_PULSE_WIDTH
        WAITCNT(Counter += CycleOffset)  
    
      REPEAT RampingSteps -= CompensationSteps
      
        PHSA := -HIGH_PULSE_WIDTH
        WAITCNT(Counter += CycleOffset += RampSpeedAdjuster)     
    
    PUB Parse
    
      'Start FullDuplexSerial
      Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
      WAITCNT(CNT + (1 * CLKFREQ))
    
      'Start FATEngine
      SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
    
      'Mount Partition
      SDCard.mountPartition(0)
    
      'Open G-Code File "MINIMUM.TXT" for reading
      SDCard.openFile(STRING("MINIMUM.TXT"), "R")
    
      REPEAT
    
        'Fill bChBuffer with 0s
        BYTEFILL(@bChBuffer, 0, 100)
    
        'Fill bValidFields with 0s
        BYTEFILL(@bValidFields, 0, 9)
    
        'Grab file lines that are not comments or blank
        GetNextValidFileLine
    
        IF(bChBuffer[0] == 0)
          QUIT
    
        'Fill in the various field elements
        FillFieldElements
        
        Terminal.Str(@bChBuffer)        
    
        'Display the various field seperations
        Terminal.Str(STRING("G="))
    
        IF(bFieldElement_0 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_0)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("M="))
    
        IF(bFieldElement_1 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_1)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("T="))
    
        IF(bFieldElement_2 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_2)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("S="))
    
        IF(bFieldElement_3 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_3)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("X="))
    
        IF(bFieldElement_4 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_4)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("Y="))
    
        IF(bFieldElement_5 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_5)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("Z="))
    
        IF(bFieldElement_6 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_6)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("F="))
    
        IF(bFieldElement_7 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_7)
        
        Terminal.Tx(32) {Space}
    
        Terminal.Str(STRING("E="))
    
        IF(bFieldElement_8 == 0)
          Terminal.Str(STRING("NULL"))
    
        ELSE
          Terminal.Str(@bFieldElement_8)
        
        Terminal.Tx(32) {Space}
    
        'Seperate G-Code field values line by line
        Terminal.Tx(13) {Carriage Return}
    
        IF STRCOMP(bChBuffer + 0, STRING("M107"))
          Terminal.Str(STRING("Fan Off"))
          Terminal.Tx(13) {Carriage Return}        
    
      'Unmount Partition
      SDCard.unmountPartition
    
      'Stop FATEngine
      SDCard.fatEngineStop
    
      WAITCNT(CNT + (1 * CLKFREQ))
    
      'Stop FullDuplexSerial  
      Terminal.Stop
    
    PUB GetNextValidFileLine
    
      REPEAT
    
        'Grab a file line as a string  
        SDCard.readString(@bChBuffer, 99)
    
        'Keep repeating while the following conditions are met,
        'because we don't want or need these lines beginning with
        'these characters.
        IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
          QUIT
    
    PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
    
      'Empty the field elements of any possible data
      BYTEFILL(@bFieldElement_0, 0, 2)
      BYTEFILL(@bFieldElement_1, 0, 3)
      BYTEFILL(@bFieldElement_2, 0, 1)
      BYTEFILL(@bFieldElement_3, 0, 3)
      BYTEFILL(@bFieldElement_4, 0, 7)
      BYTEFILL(@bFieldElement_5, 0, 7)
      BYTEFILL(@bFieldElement_6, 0, 7)
      BYTEFILL(@bFieldElement_7, 0, 7)
      'BYTEFILL(@bFieldElement_8, 0, 7) Temporary
      BYTEFILL(@bFieldElement_8, 0, 8)
    
      Index := 0
      ValueIndex := 0
    
      'Parse file line into seperate field values.  Various fields will remain empty
      REPEAT
    
        Char := bChBuffer[Index]
    
          'Exclude these characters from becoming part of are field entries
          IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
            QUIT
    
          ELSEIF(Char == 32 {Space})
            Index++
            ValueIndex := 0
            NEXT
    
          ELSEIF(Char == 71 {G Character})
    
            'Set current field element
            Field_Element := 0
            bValidFields[0] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 77 {M Character})
    
            'Set current field element
            Field_Element := 1
            bValidFields[1] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 84 {T Character})
    
            'Set current field element
            Field_Element := 2
            bValidFields[2] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 83 {S Character})
    
            'Set current field element
            Field_Element := 3
            bValidFields[3] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 88 {X Character})
    
            'Set current field element
            Field_Element := 4
            bValidFields[4] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 89 {Y Character})
    
            'Set current field element
            Field_Element := 5
            bValidFields[5] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 90 {Z Character})
    
            'Set current field element
            Field_Element := 6
            bValidFields[6] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 70 {F Character})
    
            'Set current field element
            Field_Element := 7
            bValidFields[7] := 1
            Index++
            NEXT
    
          ELSEIF(Char == 69 {E Character})
    
            'Set current field element
            Field_Element := 8
            bValidFields[8] := 1
            Index++
            NEXT
    
          ELSE
    
            IF(Field_Element == 0)
    
              'G Field element character
              bFieldElement_0[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 1)
    
              'M Field element character
              bFieldElement_1[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 2)
    
              'T Field element character
              bFieldElement_2[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 3)
    
              'S Field element character
              bFieldElement_3[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 4)
    
              'X Field element character
              bFieldElement_4[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 5)
    
              'Y Field element character
              bFieldElement_5[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 6)
    
              'Z Field element character
              bFieldElement_6[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 7)
    
              'F Field element character
              bFieldElement_7[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
            ELSEIF(Field_Element == 8)
    
              'E Field element character
              bFieldElement_8[ValueIndex] := Char
              ValueIndex++
              Index++
              NEXT
    
    PUB ElementConversion
    
    DAT
    
            Tag1 BYTE "M107",0
    
Sign In or Register to comment.