KenHorse Posted May 9, 2022 Share Posted May 9, 2022 Yea I know it's an old protocol but still... Anyone figure out how to do it in PHP? Searching the forum turns up nothing xmodem related Quote Link to comment https://forums.phpfreaks.com/topic/314771-xmodem-in-php-is-it-even-possible/ Share on other sites More sharing options...
requinix Posted May 9, 2022 Share Posted May 9, 2022 PHP support for a 45-year-old protocol? Odds aren't good. If you can find some C code for it then you could translate that into PHP code. Or if you were really serious then you could instead write a PHP extension around it. Quote Link to comment https://forums.phpfreaks.com/topic/314771-xmodem-in-php-is-it-even-possible/#findComment-1596042 Share on other sites More sharing options...
kicken Posted May 9, 2022 Share Posted May 9, 2022 Protocol seems simple enough, you could probably create an implementation of it without a lot of work. Quote Link to comment https://forums.phpfreaks.com/topic/314771-xmodem-in-php-is-it-even-possible/#findComment-1596043 Share on other sites More sharing options...
kicken Posted May 9, 2022 Share Posted May 9, 2022 (edited) I was interested since I've actually used Xmodem a couple times in the last year. Implementing the protocol was easy enough, figuring how terminal settings to make it actually work was more of a pain. It's not pretty, but here's a receive implementation (with the help of this crc library) <?php class XModem { const SOH = "\x01"; const EOT = "\x04"; const ACK = "\x06"; const NAK = "\x15"; const CAN = "\x18"; const C = "C"; private $channel; private $channelBuffer; private $outFile; private $lastPacketData; /** * @param resource $channel * @param resource $file * * @return void */ public function receive($channel, $file){ $this->channel = $channel; $this->outFile = $file; stream_set_blocking($channel, false); $lastPacketNumber = 0; do { $packet = $this->readPacket(); if (!$packet){ if ($lastPacketNumber === 0){ $this->writePacket(self::C); } else { $this->writePacket(self::ACK); } continue; } if ($packet[0] === self::SOH){ $packetNumber = $this->packetNumber(substr($packet, 1, 2)); $data = substr($packet, 3, 128); $receivedCRC = substr($packet, -2); $calculatedCRC = $this->calcCRC($data); if (($packetNumber === $lastPacketNumber + 1 || $packetNumber === 0 && $lastPacketNumber === 255) && $calculatedCRC === $receivedCRC){ $lastPacketNumber = $packetNumber; $this->updateReceiveFile($data); $this->writePacket(self::ACK); } else { $this->channelBuffer = ''; $this->writePacket(self::NAK); } } else if ($packet[0] === self::CAN){ $lastPacketNumber = 0; fseek($file, 0); ftruncate($file, 0); $this->writePacket(self::C); } else if ($packet[0] === self::EOT){ $this->finishReceiveFile(); $this->writePacket(self::ACK); } } while (!feof($channel) && !$packet || $packet[0] !== self::EOT); } private function updateReceiveFile($data){ if ($this->lastPacketData){ fwrite($this->outFile, $this->lastPacketData); } $this->lastPacketData = $data; } private function finishReceiveFile(){ fwrite($this->outFile, rtrim($this->lastPacketData, "\x1a")); } private function readPacket(){ $packetType = $this->readChannelBuffer(1); if ($packetType === self::SOH){ $pn = $this->readChannelBuffer(2); $data = $this->readChannelBuffer(128); $crc = $this->readChannelBuffer(2); $packet = $packetType . $pn . $data . $crc; } else { $packet = $packetType; } return $packet; } private function readChannelBuffer($length){ $startTime = time(); while (strlen($this->channelBuffer) < $length && time() - $startTime < 10){ if (!$this->bufferInput()){ usleep(100); } } $data = substr($this->channelBuffer, 0, $length); $this->channelBuffer = substr($this->channelBuffer, $length); return $data; } private function bufferInput(){ $byteCounter = 0; do { $r = [$this->channel]; $w = $e = []; $readable = stream_select($r, $w, $e, 0, 0); if ($readable){ $dataRead = stream_get_contents($this->channel); $byteCounter += strlen($dataRead); if ($dataRead){ $this->channelBuffer .= $dataRead; } } } while ($readable); return $byteCounter > 0; } private function writePacket($data){ $total = strlen($data); $written = 0; do { $dataToWrite = substr($data, $written); $written += fwrite($this->channel, $dataToWrite); fflush($this->channel); } while ($written < $total); } private function packetNumber($pnBytes){ $n1 = ord($pnBytes[0]); $n2 = ord($pnBytes[1]); if ($n1 + $n2 !== 255){ return null; } return $n1; } private function calcCRC($data){ $o = new mermshaus\CRC\CRC16XModem(); $o->update($data); return $o->finish(); } } Requires some terminal setup to work properly. I eventually included this code in my test script to fix terminal problems, but it creates it's own problems (can't ctrl+c the script). system('/usr/bin/stty -cooked -echo'); register_shutdown_function(function(){ system('/usr/bin/stty cooked echo'); }); I'll leave implementing send and other improvements for the reader. Edited May 9, 2022 by kicken Quote Link to comment https://forums.phpfreaks.com/topic/314771-xmodem-in-php-is-it-even-possible/#findComment-1596102 Share on other sites More sharing options...
KenHorse Posted May 9, 2022 Author Share Posted May 9, 2022 Did you ever do a SEND version? That's what I need. PHP to read a binary file and xmodem send it to an embedded processor Quote Link to comment https://forums.phpfreaks.com/topic/314771-xmodem-in-php-is-it-even-possible/#findComment-1596104 Share on other sites More sharing options...
kicken Posted May 10, 2022 Share Posted May 10, 2022 14 hours ago, KenHorse said: Did you ever do a SEND version? No, as noted in my reply: 14 hours ago, kicken said: I'll leave implementing send and other improvements for the reader. The XModem protocol for sending wouldn't be too hard to implement, just as receive wasn't. What would be more difficult is dealing with the serial connection to your device. You'd likely have to create a script that can connect to your device and issue whatever commands are necessary to get it ready to receive. That would be a more complex task and likely have to be tailor made to your device. I don't think it's impossible to do, PHP is capable of dealing with serial connections. I simply don't currently have any xmodem capable serial devices to mess with (did the receive bit over ssh), nor do I really have much free time to continue messing with it. Quote Link to comment https://forums.phpfreaks.com/topic/314771-xmodem-in-php-is-it-even-possible/#findComment-1596126 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.