PDA

View Full Version : Wanted: Xmodem upload (send) code



Oldbitcollector (Jeff)
01-11-2009, 12:10 PM
There are several projects which have incorporated Xmodem download code,
but I'm looking for Xmodem "send" to upload to another device.

Has anyone got code for this handy?

Thanks!
OBC

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
New to the Propeller?

Check out: Protoboard Introduction (http://jeffledger.googlepages.com/Protoboard_Introduction.pdf) , Propeller Cookbook 1.4 (http://ucontroller.com/Propeller%20Protoboard%20Designs%20for%20the%20Beg inner.pdf) & Software Index (http://forums.parallax.com/showthread.php?p=770318)
Updates to the Cookbook are now posted to: Propeller.warrantyvoid.us (http://propeller.warrantyvoid.us)
Got an SD card connected? - PropDOS (http://www.orrtech.net/propdos/)

Dr_Acula
01-11-2009, 01:36 PM
I don't have any spin code. This is vb.net code. (can I post that here?!)

As an aside, wouldn't it be cool to be able to translate basic to spin? Even if it were one of the earlier/simpler versions of basic? Especially since a lot of vb.net is really an older version of basic repackaged eg
Dim Input As New FileStream(Filenamepath, FileMode.Open, FileAccess.Read)
is the same as
Open "I",1,Filenamepath

So it can be simplified. Maybe even simulated? Anyway, I digress. VB.net code below, and also a description of the protocol so it makes a bit more sense.

Sub XModemSend(ByRef FName As String, ByVal TimerRestart As Boolean)
Dim Filenamepath As String
Dim j As Integer
Dim k As Integer
Dim LineOfText As String
Dim SerialString As String
Dim Counter As Integer
Dim ErrorString As String
Dim Packetnumber As Integer
Dim Checksum As Long
Dim OnesComplement As Byte
Dim ByteRead As Byte
Dim BinaryFileLength As Long
Dim BinaryFileCounter As Long
Dim Percent As Single
Dim ErrorCounter As Byte ' 0 to 10
Dim CPMFilename As String
' timer restart =true for sending via menu. False for auto send
Filenamepath = FName
Packetnumber = 1 ' 1 for the first packet (not zero)
Try
Dim Input As New FileStream(Filenamepath, FileMode.Open, FileAccess.Read)
Dim br As New BinaryReader(Input)
Label3.Text = "Error Count"
Label2.Text = "0%"
BinaryFileLength = br.BaseStream.Length() - 1
'i = Len(Filepath) + 1 + 1 ' eg c:\n8vem and a \ = 9 and 1 more for start of name only
Call Get_CPM_Filename(Filenamepath, CPMFilename)
CPMFilename = Strings.UCase(CPMFilename)
' send a <CR> (in case some rubbish characters on the screen)
LineOfText = Strings.Chr(13)
Call StringToPacket(LineOfText)
Sleep(300) ' 300 for 4800 to process the return
' erase the file in case it exists
LineOfText = "ERA " + CPMFilename + vbCrLf
Call StringToPacket(LineOfText)
'LineOfText = vbCrLf
'Call StringToPacket(LineOfText)
Sleep(300) ' 1000 for 4800
' run xmodem on the board
LineOfText = "XMODEM R " + CPMFilename + vbCrLf
Call StringToPacket(LineOfText) '
'LineOfText = vbCrLf
'Call StringToPacket(LineOfText) ' send a return/LF
Sleep(500) ' wait for text to come back before disabling timer
' 100 is a bit short, 300 works, 200 seems a bit better
' xmodem is taking 2 seconds to acknowledge so have at least this much time
' to get the text back
If TimerRestart = False Then
Call ClearInputBuffer() ' clear the buffer into the PC
End If
If TimerRestart = True Then Timer1.Enabled = False 'disable echo and all reads so can control them below
' if timerrestart=false then called from a program where it is already disabled
' now wait for the N8VEM to send a chr(21) - will still be echoing back the text for a bit
Do
SerialString = ""
If SerialPort.BytesToRead > 0 Then
SerialPort.Read(InPacket, 0, 1) ' get one byte
SerialString = Chr(InPacket(0)) ' get a single character/string/byte
End If
If InPacket(0) = 21 Then
ErrorString = "NAK"
Exit Do ' got a ^U
End If
If SerialString <> "" Then
'LineOfText = SerialString
'Call TextLines(LineOfText) ' print it out unless it is a chr21
End If
Counter = Counter + 1
Sleep(10) ' don't change this one - this is waiting for NAK
Label1.Text = "Waiting for NAK to start" + Strings.Str(Counter * 10) + "ms" ' display in milliseconds
System.Windows.Forms.Application.DoEvents() ' update the label1 message
' add a counter and timeout if nothing
If Counter > 800 Then
ErrorString = "Timeout"
Exit Do
End If
If ErrorString <> "" Then Exit Do
Loop
Label1.Text = ErrorString
' bug with vb.net - strings don't go to 255 so have to send data as bytes
Do
' act on each byte in the buffer array here
Sleep(1) ' 60 for 4800 but 1 working for 38400
'If Radio1200 = True Then Sleep(250)
XOut(0) = 1 'SOH ^A
XOut(1) = Packetnumber
OnesComplement = 255 - Packetnumber
XOut(2) = OnesComplement
Packetnumber = Packetnumber + 1
If Packetnumber > 255 Then Packetnumber = Packetnumber - 256
Checksum = 0
For i = 0 To 127 ' get 128 bytes from file
If BinaryFileCounter <= BinaryFileLength Then
ByteRead = br.ReadByte ' get a byte
Else
ByteRead = 26 ' pad with char 26 = hex 1A because hyperterm does this ? why but it works
End If
XOut(i + 3) = ByteRead
Checksum = Checksum + ByteRead
BinaryFileCounter = BinaryFileCounter + 1
Next
' create checksum
Do
If Checksum < 256 Then Exit Do ' work out checksum
Checksum = Checksum - 256
Loop
XOut(131) = Checksum
Call OutputXout()
System.Windows.Forms.Application.DoEvents() ' update the label
Do
k = 0
Do
Sleep(10) '80 for 4800 wait for the acknowledge which is a character 6 delay of 80 only occasionally loops so k>0
j = SerialPort.BytesToRead
If j >= 1 Then
SerialPort.Read(InPacket, 0, 1) ' get one byte
Exit Do
End If
k = k + 1
If k > 70 Then
InPacket(0) = 0 ' for below error testing
Exit Do
End If
Loop
If InPacket(0) = 6 Then Exit Do ' board got the packet
If InPacket(0) = 21 Or InPacket(0) = 0 Then
' didn't get it so try sending again
ErrorCounter = ErrorCounter + 1
Call OutputXout()
Label3.Text = "Error: " + Strings.Str(ErrorCounter)
System.Windows.Forms.Application.DoEvents() ' update the label
End If
Label3.Text = "Error: " + Strings.Str(ErrorCounter)
System.Windows.Forms.Application.DoEvents() ' update the label
If ErrorCounter >= 10 Then Exit Do
Loop
If ErrorCounter >= 10 Then
Label1.Text = "10 errors - aborting"
Label2.Text = "0%"
OutPacket(0) = 24 ' send a ^X cancel
SerialPort.Write(OutPacket, 0, 1) ' send 1 byte
OutPacket(0) = 3 ' send a ^C
SerialPort.Write(OutPacket, 0, 1) ' send 1 byte
Exit Do
End If
If BinaryFileCounter > BinaryFileLength Then Exit Do ' finish up
Label1.Text = "Sent packet: " + Strings.Str(Packetnumber) + " = OK"
Percent = BinaryFileCounter / BinaryFileLength
Percent = Percent * 100
Percent = Int(Percent)
Label2.Text = Strings.Str(Percent) + "%"
Loop
If ErrorCounter < 10 Then
Sleep(100)
OutPacket(0) = 4
SerialPort.Write(OutPacket, 0, 1) ' send 1 byte
Label2.Text = "100%"
Label1.Text = "Finished all OK"
End If
Input.Close()
Sleep(200) ' wait for next file
If TimerRestart = True Then Timer1.Enabled = True ' update the text - no sleeps after this
System.Windows.Forms.Application.DoEvents() ' update the labels
Catch ex As Exception
Close()
If TimerRestart = TimerRestart = True Then Timer1.Enabled = True ' update the text
System.Windows.Forms.Application.DoEvents() ' update the labels
MsgBox("No file found")
End Try
End Sub

Perception presents:
------------ Understanding The X-Modem File Transfer Protocol ---------------

by Em Decay

This has to be one of the most internationally accepted protocols for upload-
ing and downloading binary and text files. It is fairly straight-forward as
to how it is set up and there are some error checking capablities.


--- Before you begin ---

Things you need to know beforehand...

The following terms are simply ascii codes:
SOH = chr(1) = CTRL-A =
EOT = chr(4) = CTRL-D = End of Transmission
ACK = chr(6) = CTRL-F = Positive Acknowledgement
NAK = chr(21) = CTRL-U = Negative Acknowledgement
CAN = chr(24) = CTRL-X = Cancel

In order to send the file, you must first divide it into 128 byte sections
(packets). Bytes 0-127 of the file make up the first packet, bytes 128-255
make up the second packet, etc.

The packet number sent is simply the number of the packet. If the packet
number is greater than 255, then subtract 256 repeatly until the number is
between 0 and 255. For example, if you were sending packet 731, then you
would send 731 - 256 - 256 = 219.

The 1's complement of a byte (to make life easy) is simply 255 minus the
byte. For example, if you had to take the 1's complement of 142, the answer
would be 255 - 142 = 133.

The checksum is the value of all the bytes in the packet added together. For
example, if the first five bytes were 45, 12, 64, 236, 173 and the other 123
bytes were zeroes, the checksum would be 45+12+64+236+173+0+0+...+0 = 530.
However, to make each block one byte smaller, they repeatly subtract 256
from the checksum until it is between 0 and 255. In this case, the checksum
would be 530 - 256 - 256 = 18.

The first byte the downloader sends is referred to as the NCGbyte.

Provided that you aren't lost already, here is what happens next. The steps
below describe who sends what when :)


--- The Actual Transfer ---

The uploader waits until the downloader sends a NAK byte. The NAK byte
is the signal that the downloader is ready to start. This byte is referred
to as the NCGbyte. If the downloader takes too long or an error occurs then
the uploader will stop waiting or "Time Out". If this happens, then the
file transfer must restart.

With each packet sent...

The uploader sends:

1. an SOH byte {1 byte}
2. the packet number {1 byte}
3. the 1's complement of the packet number {1 byte}
4. the packet {128 bytes}
5. the checksum {1 byte}
The above five things are called the block.

The downloader:

1. ensures that the packet number sent matches the actual packet number
that it is (If the third block send has a '4' as the second byte,
something is wrong --> CANCEL TRANSFER (send CAN byte))
2. adds the packet number and the 1's complement of it together to make
sure that they add up to 255. if they don't --> CANCEL TRANSFER
3. adds up all the bytes in the packet together --> THE SUM
4. compares the last two significant digits of THE SUM with the checksum
5. if everything looks ok (sum=checksum), then the downloader appends the
bytes in the packet to the file being created (sent). The down-
loader then sends an ACK byte which tells the uploader to send the
next block.
if the sums do not match then the downloader sends an NAK byte which
tells the uploader to send the same block it just sent over again.

When the uploader sends an EOT byte instead of an SOH byte, the downloader
sends a NAK byte. If the uploader sends another EOT immediately after that,
the downloader sends an ACK byte and the transfer is complete.

Another thing, the downloader can cancel the transfer at any time by sending
a CAN byte. The uploader can only cancel between blocks by sending a CAN
byte. It is recommended that you send anywhere between 2 and 8 consecutive
CAN bytes when you wish to cancel as some programs will not let you abort if
only 1 CAN byte is sent.


--- Wrap Up ---

Hopefully, you were able to follow along. :) If not, you can e-mail me at
em_decay@norlink.net and I will try to clarify it for you. Have fun :)

Perception: Em Decay -- Mark Korhonen
Cmf ------- Chris Fillion

Written on Dec.28/95

Oldbitcollector (Jeff)
01-11-2009, 01:41 PM
Ah, that is PERFECT!

I believe I can translate that no problem.

Thanks for posting it. Once I get both halves working, I'll create an object for this as it
looks like this question is raised about twice a year. (searching: search.parallax.com)

OBC

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
New to the Propeller?

Check out: Protoboard Introduction (http://jeffledger.googlepages.com/Protoboard_Introduction.pdf) , Propeller Cookbook 1.4 (http://ucontroller.com/Propeller%20Protoboard%20Designs%20for%20the%20Beg inner.pdf) & Software Index (http://forums.parallax.com/showthread.php?p=770318)
Updates to the Cookbook are now posted to: Propeller.warrantyvoid.us (http://propeller.warrantyvoid.us)
Got an SD card connected? - PropDOS (http://www.orrtech.net/propdos/)

Dr_Acula
01-11-2009, 01:52 PM
Maybe start with the description first and code from that, rather than start with the vb.net code? The code is complicated. The description is clear, and it may well lend itself to shorter code in spin anyway.

heater
01-11-2009, 03:09 PM
The wikipedia article on XMODEM indicates that CAN (cancel) was not used in XMODEM and goes on to explain that use of CAN and many other suggestions were rejected by the original author Ward Christensen. This resulting in a plethora of incompatible "improved" protocols.

Searching around I find the original and proper XMODEM is described in chapter 7 of the attached document (by the author of YMODEM) and it states "the use of CAN was never adopted as a standard"

Chapter 4 describes use of a double CAN as an enhancement other enhancements being 16 bit CRCs etc etc.

Be ready for thousands of support calls as people try out XMODEM for the Prop with all kinds of incompatible software:)

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
For me, the past is not over yet.

heater
01-11-2009, 03:40 PM
See also this C source code cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/ttssh2/teraterm/ttpfile/xmodem.c?view=markup (http://cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/ttssh2/teraterm/ttpfile/xmodem.c?view=markup)

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
For me, the past is not over yet.

heater
01-11-2009, 06:28 PM
Of course there is nothing like the original, attached. It's very well commented.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
For me, the past is not over yet.

Oldbitcollector (Jeff)
01-12-2009, 06:57 AM
Thanks guys!

Spent a couple hours (chickening out) trying to adapt Ray's Ymodem upload to work without success.
Finally relented to writing it from scratch. (took about an hour)
A primitive version has been added to my current project (http://forums.parallax.com/showthread.php?p=772078).

OBC

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
New to the Propeller?

Check out: Protoboard Introduction (http://jeffledger.googlepages.com/Protoboard_Introduction.pdf) , Propeller Cookbook 1.4 (http://ucontroller.com/Propeller%20Protoboard%20Designs%20for%20the%20Beg inner.pdf) & Software Index (http://forums.parallax.com/showthread.php?p=770318)
Updates to the Cookbook are now posted to: Propeller.warrantyvoid.us (http://propeller.warrantyvoid.us)
Got an SD card connected? - PropDOS (http://www.orrtech.net/propdos/)

Post Edited (Oldbitcollector) : 1/12/2009 12:45:46 AM GMT