title: Flutter XModem Support via usb_serial
body: <p>I'm trying to send file via XModem on Flutter via USB Serial Communication using usb_serial package. But sometimes files are not transferred properly or completely. When I did a cross check packet gets missing from nowhere. Can anyone help me out on this?</p>
  <p>Below is the code for XModem I wrote for sending file on Flutter:</p>
  <p><strong>Note:
  It's premature code and R&amp;D is going on. Any help and changes will be really helpful.</strong></p>
  <pre><code>class XModem {
    /* Protocol characters used */
    /* Start Of Header */
    final int SOH = 0x01;
  
    /* End Of Transmission */
    final int EOT = 0x04;
  
    /* Acknowledge */
    final int ACK = 0x06;
  
    /* Negative Acknowledge */
    final int NAK = 0x15;
  
    /* Cancel */
    final int CAN = 0x18;
  
    /* Fill extra data at end if block size is less */
    final int FILLER = 0x1A;
  
    final int CRC_MODE = 0x43;
  
    /*Block Size*/
    final int block_size = 128;
  
    /*Packet Size*/
    final int packet_size = 133;
  
    /* max times to retry one block */
    final int MAXERRORS = 10;
  
    /*Current Block Number*/
    int blocknumber = 0;
  
    /*Total Blocks to Send*/
    int totalBlocks = 0;
  
    StreamSubscription? _usbStreamSubscription;
  
    // StreamSubscription? xmodemCancelTransferStreamSubscription;
    late final UsbPort _socket;
    late final BuildContext context;
    late Uint8List dataBuffer;
    bool cancelTransfer = false;
    bool sent_eof = false;
    final Function onEnd;
    final Function onTimeout;
    final Function(String error) onError;
    late final Stream _usbStream;
    List&lt;Uint8List&gt; packagedBuffer = [];
    int errorcount = 0;
    int lastChar = 0;
    bool startSending = false;
    bool isSending = false;
    bool isXModemReady = false;
  
    XModem(this.context, UsbPort socket, this.dataBuffer,
        {required this.onEnd, required this.onTimeout, required this.onError}) {
      this._socket = socket;
      this._usbStream = _socket.inputStream!;
      _usbStreamSubscription = _usbStream.listen((data) async {
        emit(&quot;RECV&quot;,data);
        if (data.length &gt; 0) {
          String x = String.fromCharCodes(data);
          terminalStreamController!.add(x);
          if(data.length &gt; 1) {
            if(startSending &amp;&amp; blocknumber &gt; 0) {
              if(isXModemReady) {
                emit(&quot;error&quot;, &quot;Error While Sending - $x&quot;);
                await cancel();
                onError(x);
                return;
              }
              isXModemReady = x.contains(&quot;xmodem ready to download the code&quot;);
            }
          }
          lastChar = data[0];
          if (blocknumber &gt; totalBlocks &amp;&amp; !isSending) {
            if (sent_eof == false) {
              sent_eof = true;
              emit('status', {'action': 'send', 'signal': 'EOT'});
              isSending = false;
              await _socket.write(new Uint8List.fromList([EOT]));
            } else {
              emit('stop', 0);
              try {
                _usbStreamSubscription!.cancel();
                _usbStreamSubscription = null;
              } catch (e) {
                print(e);
              }
              Navigator.pop(context);
              onEnd();
            }
          } else {
            if (startSending &amp;&amp; !isSending) sendNext(lastChar);
          }
        }
      });
  
      // xmodemCancelTransferStreamSubscription =
      //     xmodemCancelTransferHandler!.stream.listen((_) {
      //   cancel();
      //   emit('status', 'Cancelled');
      // });
    }
  
    Future&lt;int&gt; getchar() async {
      Uint8List x = await _usbStream.last;
      emit(&quot;getchar&quot;, x);
      return x.length &gt; 0 ? x[0] : 0;
    }
  
    Future&lt;bool&gt; flush() async {
      //_socket.inputStream!.drain();
      return true;
    }
  
    emit(String type, dynamic x) {
      print(&quot;$type ==&gt; $x&quot;);
      terminalStreamController!.add(&quot;Type: $type ==&gt; $x&quot;);
    }
  
    cancel() async {
      try {
        emit(&quot;CANCEL&quot;, &quot;CANCEL CALLED&quot;);
        // _socket.inputStream?.drain();
        _usbStreamSubscription?.cancel();
        try {
          await _socket.write(new Uint8List.fromList([CAN]));
        } catch (e) {
          print(e);
        }
        // xmodemCancelTransferStreamSubscription?.cancel();
        cancelTransfer = true;
        emit(&quot;CANCEL&quot;, &quot;CANCEL END&quot;);
      } catch (e) {
        print(e);
        emit(&quot;CANCEL&quot;, &quot;CANCEL ERROR: ${e.toString()}&quot;);
      }
    }
  
    sendNext(int lastChar) async {
      isSending = true;
      emit(&quot;send&quot;, &quot;Send Next Called&quot;);
      if (cancelTransfer) {
        emit(&quot;Cancel&quot;, &quot;Send Cancel Transfer Block&quot;);
        cancelTransfer = false;
        _usbStreamSubscription?.cancel();
        return;
      }
      errorcount = 0;
      await flush();
      // emit(&quot;lastChar&quot;, &quot;$lastChar&quot;);
      bool sendBlockStatus = false;
      if (lastChar == ACK || lastChar == NAK) {
        if(await checkEndBlock()) {
          return;
        }
        if (lastChar == NAK) {
          if (blocknumber != 0) {
            --blocknumber;
          }
        }
        emit(&quot;send&quot;, {&quot;blockNumber&quot;: blocknumber});
        sendBlockStatus = await sendBlock();
        if(sendBlockStatus) {
          await checkEndBlock(checkEquals: true);
        }
        return;
      }
      isSending = false;
    }
  
    Future&lt;bool&gt; sendBlock() async {
      Uint8List? sector = blocknumber &lt; totalBlocks ? packagedBuffer[blocknumber] : null;
      if(sector == null) {
        await checkEndBlock(checkEquals: true);
        return false;
      }
      List&lt;int&gt; sendBuffer = [SOH, blocknumber + 1, (0xFF - (blocknumber + 1))];
      sendBuffer.addAll(sector);
      int checksum = 0;
      int index = 0;
      for (index = 0; index &lt; block_size; index++) {
        checksum += sector[index];
      }
      emit(&quot;status&quot;, &quot;Block $blocknumber Sent! ($blocknumber/$totalBlocks)&quot;);
      ++blocknumber;
      isSending = false;
      sendBuffer.add(checksum);
      await putchars(sendBuffer);
      setProgress();
      return true;
    }
  
    Future&lt;bool&gt; checkEndBlock({bool checkEquals = false}) async {
      if ((checkEquals &amp;&amp; blocknumber &gt;= totalBlocks) || (!checkEquals &amp;&amp; blocknumber &gt; totalBlocks)) {
        if (sent_eof == false) {
          sent_eof = true;
          emit('status', {'action': 'send', 'signal': 'EOT'});
          isSending = false;
          await _socket.write(new Uint8List.fromList([EOT]));
        } else {
          emit('stop', 0);
          try {
            _usbStreamSubscription!.cancel();
            _usbStreamSubscription = null;
          } catch (e) {
            print(e);
          }
          Navigator.pop(context);
          onEnd();
          // await socket.close();
          // socket.close('data', sendData);
        }
        return true;
      }
      return false;
    }
  
    send() async {
      emit(&quot;message&quot;, &quot;Send Called&quot;);
      blocknumber = 1;
      emit(&quot;message&quot;, &quot;Prepare Packaged Buffer&quot;);
  
      Uint8List currentBlock = new Uint8List(block_size);
      while (dataBuffer.length &gt; 0) {
        for (int i = 0; i &lt; block_size; i++) {
          if (i &lt; dataBuffer.length) {
            currentBlock[i] = dataBuffer[i];
          } else {
            currentBlock[i] = FILLER;
          }
        }
        if (dataBuffer.length &gt; block_size)
          dataBuffer = dataBuffer.sublist(block_size);
        else
          dataBuffer = Uint8List(0);
        packagedBuffer.add(currentBlock);
        currentBlock = new Uint8List(block_size);
      }
  
      totalBlocks = packagedBuffer.length;
      emit(&quot;send&quot;, &quot;Total Blocks: $totalBlocks&quot;);
  
      emit(&quot;Transmission Begin&quot;, &quot;&quot;);
  
      startSending = true;
      if(!isSending) {
        if (lastChar == ACK || lastChar == NAK) {
          sendNext(lastChar);
        }
      }
    }
  
    Future&lt;void&gt; putchar(int data) async {
      await _socket.write(Uint8List.fromList([data]));
    }
  
    Future&lt;void&gt; putchars(List&lt;int&gt; data) async {
      await _socket.write(Uint8List.fromList(data));
    }
  
    setProgress() {
      double progress = (blocknumber - 1) / totalBlocks;
      progressStreamHandler!.add(progress);
    }
  }
  
  </code></pre>