diff --git a/curl.sh b/curl.sh deleted file mode 100644 index b05d38b..0000000 --- a/curl.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -#This script just downloads the firmware from the new matter store -#This script is adapted for a websocket fronted and processes -#$_POST data expected ?url= -#Evaluating that query string results in setting the variable "url" to the actual intended download url -#kind of hacky but really the only way to interface with websockets. Needs sanitization etc. -#echo for debugging, eval to actually put the variable in -echo $QUERY_STRING - -eval $QUERY_STRING - -#Another echo for debugging, let's make sure we have a url variable now -echo $url - -#start downloading, log the download progress, and fork into the background -curl -# --limit-rate 5K --output /dev/null $url > /tmp/curl.log 2>&1 & - -#capture the PID for the while loop -pid=$! - -#Loop while curl is still alive -while kill -0 $pid 2>/dev/null; do - #We just need the progress percentage - progress=`grep -ao '[^ ]*%' /tmp/curl.log | tail -1` - #Send that over to the webpage - echo $progress - #Doesn't need to update very often - sleep 0.1 -done - -#We won't always capture the 100% mark, so we force it if we make it this far. -#Suggested fix: check if $pid curl exits gracefully. use try...catch statement to handle HTTP response codes -echo "100%" - -/* - *TODO: The above is ugly, and breaks easily - *Need to implement error handling, etc, what if the server is down? - *No internet? What if the data passed to the url variable is malformed? - */ diff --git a/enter_dfu.py b/enter_dfu.py deleted file mode 100644 index 084d90c..0000000 --- a/enter_dfu.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/python -import sys -import os -import usb.core -import usb.util -import time -#This script just finds a mod-t and puts it in DFU mode. - - -dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) - -#If we didn't find a Mod-T we need to throw an error -if dev is None: - raise ValueError('No Mod-T detected') - -#Set active configuration (first is default) -dev.set_configuration() - -#First we mimmick the Mod-T desktop utility -#The initial packet is not human readable -#The second packet puts the Mod-T into DFU mode -dev.write(2, bytearray.fromhex('246a0095ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":7},"data":{"command":{"idx":53,"name":"Enter_dfu_mode"}}};') - -#Wait for the Mod-T to reattach in DFU mode -time.sleep(2) - diff --git a/flash_firmware.sh b/flash_firmware.sh index 8e5b6fe..b2adc76 100644 --- a/flash_firmware.sh +++ b/flash_firmware.sh @@ -2,7 +2,13 @@ #This script actually runs dfu-util to flash firmware to the Mod-T #Currently the firmware version and location is hard-coded for testing purposes. Eventually this should be changed to $1 #Actually start writing the firmware, in the background, and log to a file. -dfu-util -d 2b75:0003 -a 0 -s 0x0:leave -D /home/xaero/Downloads/firmware_modt_override.dfu > /tmp/dfu & + +if [ "$1" == "" ]; then + echo "No DFU-file specified" + exit 1 +fi + +dfu-util -d 2b75:0003 -a 0 -s 0x0:leave -D $1 > /tmp/dfu & #Loop until the firmware has been written while true; do @@ -25,4 +31,3 @@ done #cleanup our temporary file rm /tmp/dfu - diff --git a/fw_update.py b/fw_update.py deleted file mode 100644 index 38752a9..0000000 --- a/fw_update.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/python -import sys -import os -import usb.core -import usb.util -import time -#This script *SHOULD* eventually be the all-encompassing firmware update -#It should call the check FW script, place the Mod-T into DFU mode and flash the firmware -#It should have a command-line arg to flash older firmware versions, none of this is really implemented yet -#Make sure this was called correctly -if not len(sys.argv)==2: - print("Usage: fw_update.py filename.dfu") - quit() - -#Find the Mod-T - we should probably see if it's in DFU mode, too -#That way we can do emergency flashes from recovery mode -dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) - -#If we didn't find a Mod-T we need to throw an error -if dev is None: - raise ValueError('No Mod-T detected') - -#Make sure the filename supplied is actually a file, and error out appropriately -fname=str(sys.argv[1]) -if not os.path.isfile(fname): - print(fname + " not found") - quit() - -#Set active configuration (first is default) -dev.set_configuration() - -#First we mimmick the Mod-T desktop utility -#The initial packet is not human readable -#The second packet puts the Mod-T into DFU mode -dev.write(2, bytearray.fromhex('246a0095ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":7},"data":{"command":{"idx":53,"name":"Enter_dfu_mode"}}};') - -#Wait for the Mod-T to reattach in DFU mode -time.sleep(2) - diff --git a/latest_firmware.py b/latest_firmware.py deleted file mode 100644 index 5bbcee0..0000000 --- a/latest_firmware.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/python2 - -# This script just reaches out to the New Matter server and asks what the latest firmware is. -# Since their page returns JSON we get the name of the firmware as well as the URL to it as result. -import re -import os.path -import urllib2 -import base64 -import gzip -import zlib -from StringIO import StringIO -from io import BytesIO - -def make_requests(): - response = [None] - responseText = None - - if(request_de_newmatter_com(response)): - responseText = read_response(response[0]) - print responseText - response[0].close() - - -def read_response(response): - if response.info().get('Content-Encoding') == 'gzip': - buf = StringIO(response.read()) - return gzip.GzipFile(fileobj=buf).read() - - elif response.info().get('Content-Encoding') == 'deflate': - decompress = zlib.decompressobj(-zlib.MAX_WBITS) - inflated = decompress.decompress(response.read()) - inflated += decompress.flush() - return inflated - - return response.read() - - -def request_de_newmatter_com(response): - response[0] = None - - try: - req = urllib2.Request("https://de.newmatter.com/api/fw/racingmoon/version?v=2&sid=C14BUVBXBlNWUgRVBgQKCFNTBwUHBQMA") - req.add_header("Authorization", "Basic NDVkMDJiZGU1NjhkNmQwMWJmNjM3ZmNmZWJjM2FjODU6OTkwMzZjZTQzZTZiNTk5ZTNhNDc2NTJjZjRiNTc3ZWUyNGQyOTI0NzIxNGU2M2UxM2UwZDQ0N2IwMzg4NzY3ZA==") - req.add_header("User-Agent", "Mozilla 5.0 (MOD-t printer tool 1.4.2)") - req.add_header("Content-Type", "application/json") - req.add_header("Connection", "Keep-Alive") - req.add_header("Accept-Encoding", "gzip, deflate") - req.add_header("Accept-Language", "en-US,*") - - response[0] = urllib2.urlopen(req) - - except urllib2.URLError, e: - if not hasattr(e, "code"): - return False - response[0] = e - except: - return False - - return True - - -make_requests() diff --git a/load_filament.py b/load_filament.py deleted file mode 100644 index c7218e7..0000000 --- a/load_filament.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - -#Just tells the mod-t to enter load filament mode. It also polls the mod-t for status -#This status information can be used to prompt the user to proceed as intended. - -import sys -import os -import usb.core -import usb.util -import time - -# Read pending data from MOD-t (bulk reads of 64 bytes) -def read_modt(ep): - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = text - while len(text)==64: - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = fulltext + text - return fulltext - -#Find the Mod-T - we should probably see if it's in DFU mode, too -#That way we can do emergency flashes from recovery mode -dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) - -#If we didn't find a Mod-T we need to throw an error -if dev is None: - raise ValueError('No Mod-T detected') - -#Set active configuration (first is default) -dev.set_configuration() - -#Same as all the other files, this packet is not readable -#The second packet however, is - -dev.write(2, bytearray.fromhex('24690096ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":9},"data":{"command":{"idx":52,"name":"load_initiate"}}};') - -while True: - dev.write(4, '{"metadata":{"version":1,"type":"status"}}') - print(read_modt(0x83)) - time.sleep(5) diff --git a/modt.py b/modt.py new file mode 100644 index 0000000..11b5c4f --- /dev/null +++ b/modt.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 + +import argparse +import usb.core +import usb.util +import time +from zlib import adler32 + +class GCODES: + CLEAR_NOZZLE = ''' + M82 + M109 S220.0 ;set temp and wait + G0 Z100 F300 ; move to Z 100 when @ temp + G1 E10.0000 F9000.00000 ; extrude some filament + G0 Z50 F100 ; move down slowly after extruding + ; https://github.com/ajfoul/MOD-t/blob/master/clearnozzle.gcode + ''' + +class ModT: + class PAYLOADS: + BIO_GET_VERSION = (2, '{"transport":{"attrs":["request","twoway"],"id":3},"data":{"command":{"idx":0,"name":"bio_get_version"}}};') + ENTER_DFU_MODE = (2, '{"transport":{"attrs":["request","twoway"],"id":7},"data":{"command":{"idx":53,"name":"Enter_dfu_mode"}}};') + LOAD_INITIATE = (2, '{"transport":{"attrs":["request","twoway"],"id":9},"data":{"command":{"idx":52,"name":"load_initiate"}}};') + STATUS = (4, '{"metadata":{"version":1,"type":"status"}}') + UNLOAD_INITIATE = (2, '{"transport":{"attrs":["request","twoway"],"id":11},"data":{"command":{"idx":51,"name":"unload_initiate"}}};') + WIFI_CLIENT_GET_STATUS = (2, '{"transport":{"attrs":["request","twoway"],"id":5},"data":{"command":{"idx":22,"name":"wifi_client_get_status","args":{"interface_t":0}}}};') + + def __init__(self): + #Find the Mod-T - we should probably see if it's in DFU mode, too + #That way we can do emergency flashes from recovery mode + self.dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) + + #If we didn't find a Mod-T we need to throw an error + if self.dev is None: + raise ValueError('No Mod-T detected') + + #Set active configuration (first is default) + self.dev.set_configuration() + + self.dev.write(2, bytearray.fromhex('246c0093ff')) + + def write(self, endpoint, message): + self.dev.write(endpoint, message) + + def write_gcode(self, gcode, print_progress=False, print_status=False, print_blocks=False, encoding='utf8'): + if not isinstance(gcode, bytes): + gcode = bytes(gcode, encoding) + gcode_len = len(gcode) + + def adler32_hash(): + blocksize = 256*1024*1024 + hash = 0 + for ptr in range(0, gcode_len, blocksize): + end = min(ptr+blocksize, gcode_len) + data = gcode[ptr:end] + hash = adler32(data, hash) + if hash < 0: + hash += 2**32 + return hash + + hash = adler32_hash() + self.write(4, '{"metadata":{"version":1,"type":"file_push"},"file_push":{"size":'+str(gcode_len)+',"adler32":'+str(hash)+',"job_id":""}}') + + # submit gcode in block and retrieve status every 20 blocks + status_frequency = 20 + blocksize = 5120 + status_ctx = 0 + for ptr in range(0, gcode_len, blocksize): + status_ctx += 1 + if status_ctx > 20: + status = self.read(0x83) + if print_status: + print('# printer-status:') + print(status) + status_ctx = 0 + + end = min(gcode_len, ptr+blocksize) + block = gcode[ptr:end] + if print_progress: + print(f'# progress: {round(100*ptr/gcode_len, 3)}%, block: {ptr}-{end} of {gcode_len}, block-size: {len(block)}') + if print_blocks: + print(block) + + self.write(4, block) + + def write_gcode_file(self, filename, *args, **kwargs): + with open(filename, 'rb') as f: + gcode = f.read() + self.write_gcode(gcode, *args, **kwargs) + + def read(self, ep): + text = ''.join(map(chr, self.dev.read(ep, 64))) + fulltext = text + while len(text) == 64: + text = ''.join(map(chr, self.dev.read(ep, 64))) + fulltext = fulltext + text + return fulltext + + def get_status(self): + self.write(*self.PAYLOADS.STATUS) + return self.read(0x83) + + def print_status(self, loop=False, loop_sleep=5): + first_run = True + while loop or first_run: + print(self.get_status()) + if not loop: + break + time.sleep(loop_sleep) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Interact with a NewMatter Mod-T printer.') + parser.add_argument('--no-status-loop', help='Do not print the printers status in a loop', action='store_true') + + subparsers = parser.add_subparsers(title='available sub-commands', dest='subcmd') + subparsers.add_parser('bio_version', help='Get bio version. Seems to be equal to status.') + subparsers.add_parser('clear_nozzle', help='Execute gcode to clear the nozzle') + subparsers.add_parser('enter_dfu', help='Enter dfu mode') + + parser_fwupdate = subparsers.add_parser('firmware_update', help='Update firmware. Not implemented. Check https://github.com/tripflex/MOD-t for firmware-versions.') + parser_fwupdate.add_argument('file', help='DFU file containing the firmware.') + + subparsers.add_parser('load_filament', help='Load filament') + + parser_gcode = subparsers.add_parser('send_gcode', help='Send the contents of a gcode-file to the printer') + parser_gcode.add_argument('file', help='Path to the gcode-file. Must be utf8 encoded.') + parser_gcode.add_argument('--print-blocks', help='Print submitted blocks to screen', action='store_true') + parser_gcode.add_argument('--print-progress', help='Print the current progress for every submitted block', action='store_true') + parser_gcode.add_argument('--print-status', help='Print the printers status every 20 blocks', action='store_true') + + subparsers.add_parser('status', help='Retrieve the printers status. This is the default action.') + subparsers.add_parser('unload_filament', help='Unload filament') + subparsers.add_parser('wifi_status', help='Get wifi client status. Seems to be equal to status.') + + args = parser.parse_args() + + cmd_map = { + 'bio_version': (ModT.PAYLOADS.BIO_GET_VERSION, 0x81), + 'enter_dfu': (ModT.PAYLOADS.ENTER_DFU_MODE, None), + 'load_filament': (ModT.PAYLOADS.LOAD_INITIATE, None), + #'status': ModT.PAYLOADS.STATUS, + 'unload_filament': (ModT.PAYLOADS.UNLOAD_INITIATE, None), + 'wifi_status': (ModT.PAYLOADS.WIFI_CLIENT_GET_STATUS, 0x81) + } + + try: + printer = ModT() + except ValueError as err: + print(str(err)) + quit(1) + + if args.subcmd in cmd_map: + wargs, rendpoint = cmd_map[args.subcmd] + print(f'endpoint: {wargs[0]},', f'request: {wargs[1]}') + printer.write(*wargs) + if rendpoint is not None: + print(printer.read(rendpoint)) + + elif args.subcmd == 'clear_nozzle': + print('Clearing nozzle.') + print('Please press the start button when you see the STATE_JOB_QUEUED message') + print('using gcode:') + print(GCODES.CLEAR_NOZZLE) + printer.write_gcode(GCODES.CLEAR_NOZZLE) + + elif args.subcmd == 'send_gcode': + printer.write_gcode_file( + args.file, + print_progress = args.print_progress, + print_status = args.print_status, + print_blocks = args.print_blocks + ) + + elif args.subcmd == 'firmware_update': + #This script *SHOULD* eventually be the all-encompassing firmware update + #It should call the check FW script, place the Mod-T into DFU mode and flash the firmware + #It should have a command-line arg to flash older firmware versions, none of this is really implemented yet + printer.write(*ModT.PAYLOADS.ENTER_DFU_MODE) + + printer.print_status(loop=(not args.no_status_loop)) diff --git a/modt_status.py b/modt_status.py deleted file mode 100644 index 19277a7..0000000 --- a/modt_status.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -# Requires pyusb and permissions to read/write the mod-t via USB. -# Just polls the Mod-T for status JSON - -import sys -import os -import usb.core -import usb.util -import time - -# Read pending data from MOD-t (bulk reads of 64 bytes) -def read_modt(ep): - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = text - while len(text)==64: - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = fulltext + text - return fulltext - -# Find MOD-t usb device -dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) - -# was it found? -if dev is None: - raise ValueError('Device not found') - -#Finally, loop and query mod-t status every 5 seconds -while True: - dev.write(4, '{"metadata":{"version":1,"type":"status"}}') - print(read_modt(0x83)) - time.sleep(5) \ No newline at end of file diff --git a/readme.md b/readme.md index 9c73a5c..1f38231 100644 --- a/readme.md +++ b/readme.md @@ -1,32 +1,32 @@ -##First and foremost: +# First and foremost: **USE THESE SCRIPTS AT YOUR OWN RISK!** These scripts aren't very well tested and almost none of them are completely finished. They work to interface with the printer but it's all very manual. -##Overview and license +# Overview and license This is a basic set of utility scripts to interface with the New Matter Mod-T 3d printer on Linux. You are free to fork and contribute as you see fit. This work falls under the MIT license. See the LICENSE file for more information. -I must also credit /u/modtdev on reddit for the work on getting the gcode sent to the Mod-T +I must also credit /u/modtdev on reddit for the work on getting the gcode sent to the Mod-T. +# Depdencies +1. `dfu-util` (only for maintenance) +2. `python3` +3. `python3-pyusb` (`python3 -m pip install pyusb`) -###Depdencies -1. curl -2. *dfu-util -3. *python3 -4. *python3-pyusb - - -##Usage +# Usage First, I recommend creating a udev rule for the Mod-T similar to the following: +``` /etc/udev/rules.d/51-modt.rules: SUBSYSTEM=="usb", ATTR{idVendor}=="2b75", ATTR{idProduct}=="0002", GROUP="users", MODE="0674" -SUBSYSTEM=="usb", ATTR{idVendor}=="2b75", ATTR{idProduct}=="0003", GROUP="users", MODE=0674" + SUBSYSTEM=="usb", ATTR{idVendor}=="2b75", ATTR{idProduct}=="0003", GROUP="users", MODE="0674" +``` In the above I have everyone in the `users` group enabled, however you can restrict access as needed. This allows a regular user to run the scripts and still have them operate as intended. +To interact with the ModT-printer just mark `modt.py` as executable or invoke it with `python3 modt.py`. A list of available commands can be retrieved with `python3 modt.py -h`. The tool will print the printers status every 5 seconds in an endless loop to console unless you set the `--no-status-loop` argument. -From there most scripts are pretty straightforward. Simply flag them executable and run them. The `send_gcode.py`, `flash_firmware.sh`, and `fw_update.py` are the only scripts requiring arguments, each of them expect a single argument containing the path to an appropriate file for the function requested. +`flash_firmware.sh` requires one argument specifying the DFU-update file. -If you do choose to use these scripts to print things by sending gcode, monitor the output of the `send_gcode.py` script until you see `STATE_JOB_QUEUED` as the printer status. If you press the front panel button prior to seeing this state you will have a broken print job, which will not print correctly. +If you do choose to use these scripts to print things by sending gcode, monitor the output of the `send_gcode` command until you see `STATE_JOB_QUEUED` as the printer status. If you press the front panel button prior to seeing this state you will have a broken print job, which will not print correctly. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6513d5e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyusb diff --git a/send_gcode.py b/send_gcode.py deleted file mode 100644 index 9786c8d..0000000 --- a/send_gcode.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python - -# USAGE: send_gcode.py file.gcode -# Requires read/write permission to the Mod-T via USB. - -import sys -import os -import usb.core -import usb.util -import time -from zlib import adler32 - -# Adler32 checksum function -# Based on https://gist.github.com/kofemann/2303046 -# For some reason, mod-t uses 0, not 1 as the basis of the adler32 sum -BLOCKSIZE=256*1024*1024 - -def adler32_checksum(fname): - asum = 0 - f = open(fname, "rb") - while True: - data = f.read(BLOCKSIZE) - if not data: - break - asum = adler32(data, asum) - if asum < 0: - asum += 2**32 - f.close() - return asum - -# Read pending data from MOD-t (bulk reads of 64 bytes) -def read_modt(ep): - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = text - while len(text)==64: - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = fulltext + text - return fulltext - -# Main program -if __name__ == '__main__': - - if not len(sys.argv)==2: - print("Usage: send_gcode.py filename.gcode") - quit() - -# Read filename argument and check that the file exists -fname=str(sys.argv[1]) -if not os.path.isfile(fname): - print(fname + " not found") - quit() - -# Get the size of the gcode file -size = os.path.getsize(fname) - -# Get the adler32 checksum of the gcode file -checksum=adler32_checksum(fname) - -# Open gcode file and read into buffer -f = open(fname, "rb") -gcode = f.read() -f.close() - -# Find MOD-t usb device -dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) - -# was it found? -if dev is None: - raise ValueError('Device not found') - -# set the active configuration. With no arguments, the first -# configuration will be the active one -dev.set_configuration() - -# These came from usb dump. -# Some commands are human readable some are maybe checksums -dev.write(2, bytearray.fromhex('246a0095ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":3},"data":{"command":{"idx":0,"name":"bio_get_version"}}};') -print(read_modt(0x81)) - -dev.write(4, '{"metadata":{"version":1,"type":"status"}}') -print(read_modt(0x83)) - -dev.write(2, bytearray.fromhex('248b0074ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":5},"data":{"command":{"idx":22,"name":"wifi_client_get_status","args":{"interface_t":0}}}};') -print(read_modt(0x81)) - -dev.write(2, bytearray.fromhex('246a0095ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":7},"data":{"command":{"idx":0,"name":"bio_get_version"}}};') -print(read_modt(0x81)) - -dev.write(4, '{"metadata":{"version":1,"type":"status"}}') -print(read_modt(0x83)) - -dev.write(4, '{"metadata":{"version":1,"type":"status"}}') -print(read_modt(0x83)) - -# Start writing actual gcode -# File size and adler32 checksum calculated earlier -dev.write(4, '{"metadata":{"version":1,"type":"file_push"},"file_push":{"size":'+str(size)+',"adler32":'+str(checksum)+',"job_id":""}}') - -# Write gcode in batches of 20 bulk writes, each 5120 bytes. -# Read mod-t status between these 20 bulk writes - -start=0 -counter=0 -while True: - if (start+5120-1>size-1): - end=size - else: - end=start+5120 - block = gcode[start:end] - print(str(counter)+':' +str(start)+'-'+str(end-1)+'\t'+str(len(block))) - counter += 1 - if counter>=20: - temp=read_modt(0x83) - counter = 0 - dev.write(4, block) - if (start == 0): - temp=read_modt(0x83) - start = start + 5120 - if (start>size): - break; - -# Gcode sent. Finally, loop and query mod-t status every 5 seconds -while True: - dev.write(4, '{"metadata":{"version":1,"type":"status"}}') - print(read_modt(0x83)) - time.sleep(5) \ No newline at end of file diff --git a/unload_filament.py b/unload_filament.py deleted file mode 100644 index 296d457..0000000 --- a/unload_filament.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - -#Just tells the mod-t to enter unload filament mode. It also polls the mod-t for status -#This status information can be used to prompt the user to proceed as intended. - -import sys -import os -import usb.core -import usb.util -import time - -# Read pending data from MOD-t (bulk reads of 64 bytes) -def read_modt(ep): - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = text - while len(text)==64: - text=''.join(map(chr, dev.read(ep, 64))) - fulltext = fulltext + text - return fulltext - -#Find the Mod-T - we should probably see if it's in DFU mode, too -#That way we can do emergency flashes from recovery mode -dev = usb.core.find(idVendor=0x2b75, idProduct=0x0002) - -#If we didn't find a Mod-T we need to throw an error -if dev is None: - raise ValueError('No Mod-T detected') - -#Set active configuration (first is default) -dev.set_configuration() - -#Same as all the other files, this packet is not readable -#The second packet however, is - -dev.write(2, bytearray.fromhex('246c0093ff')) -dev.write(2, '{"transport":{"attrs":["request","twoway"],"id":11},"data":{"command":{"idx":51,"name":"unload_initiate"}}};') - -while True: - dev.write(4, '{"metadata":{"version":1,"type":"status"}}') - print(read_modt(0x83)) - time.sleep(5)