Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRC Error on my client when uploading #227

Open
estraco opened this issue Dec 29, 2021 · 4 comments
Open

CRC Error on my client when uploading #227

estraco opened this issue Dec 29, 2021 · 4 comments

Comments

@estraco
Copy link

estraco commented Dec 29, 2021

Hi, I have been looking for an sdk for the MP Voxel / FlashForge Adventurer 3 for nodejs and have found nothing decent but this. I am trying to make a server so I can print anything, anywhere. The code that I have copied and modified that for some reason, won't work. It works in your app, but not mine. The files below are the only ones used. Is there any way you could help me? Thanks!

conn.ts

import net from 'net';
import { EventEmitter } from 'events';
import codes from './codes';
import { crc32 } from 'crc';
import fs from 'fs';

class Connection extends EventEmitter {
    socket: net.Socket;
    conopt = {
        host: '',
        port: 0,
    }
    sending_gcode = false;

    constructor(host: string, port: number) {
        super();
        this.socket = new net.Socket();

        this.conopt = { host, port }

        this.socket.on('data', (data) => {
            this.emit('data', data);
            const d = this.matchData(data);
            if (d.cmd === 'M105') {
                d.data = {
                    extruder: parseInt(d.data.match(/T0:([0-9]+)/)![1]),
                    bed: parseInt(d.data.match(/B:([0-9]+)/)![1]),
                }
            }
            this.emit(d.cmd, d.data);
            this.emit(codes[d.cmd], d.data);
        });

        this.socket.on('end', () => {
            this.emit('end');
        });
    }

    getTemperature() {
        this.socket.write('~M105\n');
    }

    waitFor(cmd: string) {
        return new Promise((resolve) => {
            this.socket.on('data', (data) => {
                if (data.toString().startsWith(cmd)) {
                    resolve(data);
                }
            });
        });
    }

    matchData(data: Buffer): {
        cmd: string,
        data: any
    } {
        try {
            const cmd = data.toString().match(new RegExp('CMD (?<CommandId>[MG][0-9]+) Received\.'));
            if (cmd && cmd.groups && cmd.groups.CommandId) {
                if (cmd.groups.CommandId === 'M28') this.sending_gcode = true;
                if (cmd.groups.CommandId === 'M29') this.sending_gcode = false;
            }
            if (this.sending_gcode) return { cmd: 'M28', data: data.toString() };
            return {
                cmd: cmd!.groups!.CommandId,
                data: data.toString().replace(new RegExp('CMD (?<CommandId>[MG][0-9]+) Received\.\r\n'), '')
            }
        } catch (e) {
            console.log(e);
            return {
                cmd: '',
                data: ''
            }
        }
    }

    writeCode(code: string, data: string = '') {
        this.socket.write(`~${codes[code]} ${data}\r\n`);
    }

    connect() {
        this.socket.connect(this.conopt)
    }

    printGCode(gcode: Buffer, name: string) {
        console.log(gcode.length, name);
        this.writeCode('file_start', `${gcode.length} 0:/user/${name}`);
        let count = 0;
        let offset = 0;
        while (offset < gcode.length) {
            let crc: number;
            let packet: Buffer;

            let dataSize = 0;
            if (offset + 4096 < gcode.length) {
                packet = gcode.subarray(offset, offset + 4096);

                const crcResult = crc32(packet);
                crc = crcResult;
                console.log(crcResult.toString(16));
                dataSize = 4096;
            }
            else {
                // Every packet needs to be the same size, so zero pad the last one if we need to.
                const actualLength = gcode.length - offset;
                const data = gcode.subarray(offset, actualLength + offset);

                // The CRC is for the un-padded data.
                const crcResult = crc32(data);
                crc = crcResult;
                console.log(crc.toString(16));

                packet = Buffer.alloc(4096);
                for (let i = 0; i < data.length; ++i) {
                    packet.writeUInt32LE(data[i], i);
                }

                packet.fill(0, actualLength, 4096);

                dataSize = actualLength;
            }

            // Always start each packet with four bytes
            const bufferToSend = Buffer.alloc(4096 + 16);
            bufferToSend.writeUInt16LE(0x5a, 0);
            bufferToSend.writeUInt16LE(0x5a, 1);
            bufferToSend.writeUInt16LE(0xef, 2);
            bufferToSend.writeUInt16LE(0xbf, 3);

            // Add the count of this packet, the size of the data it in (not counting padding) and the CRC.
            bufferToSend.writeUInt32BE(count, 4);
            bufferToSend.writeUInt32BE(dataSize, 8);
            bufferToSend.writeUInt32BE(crc, 12);

            // Add the data
            for (let i = 0; i < packet.length; ++i) {
                bufferToSend.writeUInt8(packet[i], i + 16);
            }

            // Send it to the printer
            fs.appendFileSync('log.txt', `Sending packet ${count} ---\n${bufferToSend}\n---\n`);
            this.socket.write(bufferToSend);

            offset += 4096;
            ++count;
        }

        this.socket.write('\n');
        this.writeCode('file_end');
    }
}

export default Connection;

codes.ts

export default {
    M601: 'start',
    M602: 'end',
    M115: 'info',
    M119: 'endstop',
    M105: 'temp',
    M27: 'status',
    M146: 'led',
    M23: 'print',
    M36: 'stop',
    M28: 'file_start',
    M29: 'file_end',
    M108: 'stop_heat',
    M651: 'fan_on',
    M652: 'fan_off',
    M114: 'position',
    M112: 'estop',
    M18: 'motor_stop',
    M17: 'motor_start',
    G92: 'set_zero',
    M610: 'change_name',
    G1: 'bed_move',
    M140: 'bed_temp',
    M104: 'extruder_temp',
    start: 'M601',
    end: 'M602',
    info: 'M115',
    endstop: 'M119',
    temp: 'M105',
    status: 'M27',
    led: 'M146',
    print: 'M23',
    stop: 'M36',
    file_start: 'M28',
    file_end: 'M29',
    stop_heat: 'M108',
    fan_on: 'M651',
    fan_off: 'M652',
    position: 'M114',
    estop: 'M112',
    motor_stop: 'M18',
    motor_start: 'M17',
    set_zero: 'G92',
    change_name: 'M610',
    bed_move: 'G1',
    bed_temp: 'M140',
    extruder_temp: 'M104',
} as Record<string, string>;
@andycb
Copy link
Owner

andycb commented Dec 29, 2021

If you're getting a CRC error from the printer, my first guess would be that you're calculating a different type of CRC. Possibly you're using a different crc node package that I did. A simple test would be to calculate the CRC of a known Hello or something and compare to to here: https://crccalc.com/

Beyond that, are you able to identify where the issue happens, is it on all packets or just the last one, if it's just the last one, it might be worth diffing into the padding a bit more.

@estraco
Copy link
Author

estraco commented Jan 3, 2022

Thank you for this help. I calculated the crc32 of 123456789 and it returned the same number as the first one on the site. It seems that I am not calculating the correct part of the gcode. It is a problem with all of the packets. The crc32 that the MP FlashPrint software sent was ce 54 58 05, but mine was 4a 51 98 c1. Also it seems like the first four bytes were changed to 5a 5a a5 a5

@estraco
Copy link
Author

estraco commented Jan 4, 2022

Ok, so I got it to not give me a CRC Error, although now it gives me the notification, "abort file transfer" It was also an issue with the last packet where I somehow didn't catch myself when I was calculating the padding in the CRC also. Here is the updated code in conn.ts:

import net from 'net';
import { EventEmitter } from 'events';
import codes, { Code } from './codes';
import { crc32 } from 'crc';
import fs from 'fs';

class Connection extends EventEmitter {
    socket: net.Socket;
    conopt = {
        host: '',
        port: 0,
    }
    sending_gcode = false;
    chunksize = 4096;

    constructor() {
        super();
        this.socket = new net.Socket();

        this.socket.on('data', (data) => {
            this.emit('data', data);
            const d = this.matchData(data);
            if (d.cmd === 'M105') {
                d.data = {
                    extruder: {
                        current: parseInt(d.data.match(/T0:([0-9]+)/)![1]),
                        target: parseInt(d.data.match(/T0:[0-9]+\s+\/([0-9]+)/)![1])
                    },
                    bed: {
                        current: parseInt(d.data.match(/B:([0-9]+)/)![1]),
                        target: parseInt(d.data.match(/B:[0-9]+\/([0-9]+)/)![1])
                    },
                }
            }
            if (d.cmd === codes['info']) {
                d.data = d.data.replace('\r\nok\r\n', '');
                const split = d.data.split('\r\n');
                d.data = {}
                for (let i = 0; i < split.length; i++) {
                    const s = split[i].split(': ');
                    if (s.length === 2) {
                        d.data[s[0].toLowerCase().split(' ').join('_')] = s[1];
                    }
                }
            }
            // console.log(d);
            this.emit(d.cmd as Code, d.data);
            this.emit(codes[d.cmd as Code], d.data);
            this.emit('event', d.cmd as Code, d.data);
        });

        this.socket.on('end', () => {
            this.emit('end');
        });
    }

    getTemperature() {
        this.socket.write('~M105\r\n');
    }

    waitFor(cmd: string) {
        return new Promise((resolve) => {
            this.socket.on('data', (data) => {
                if (data.toString().startsWith(cmd)) {
                    resolve(data);
                }
            });
        });
    }

    matchData(data: Buffer): {
        cmd?: string,
        data: any
    } {
        try {
            const cmd = data.toString().match(new RegExp('CMD (?<CommandId>[MG][0-9]+) Received\.'));
            if (cmd && cmd.groups && cmd.groups.CommandId) {
                if (cmd.groups.CommandId === 'M28') this.sending_gcode = true;
                if (cmd.groups.CommandId === 'M29') this.sending_gcode = false;
            }
            if (this.sending_gcode) return { cmd: 'M28', data: data.toString() };
            return {
                cmd: cmd?.groups?.CommandId,
                data: data.toString().replace(new RegExp('CMD (?<CommandId>[MG][0-9]+) Received\.\r\n'), '')
            }
        } catch (e) {
            console.log(e);
            return {
                cmd: '',
                data: ''
            }
        }
    }

    writeCode(code: string, data?: string) {
        this.socket.write(`~${codes[code]}${data ? ' ' + data : ''}\r\n`);
    }

    async printGCode(gcode: Buffer, name: string) {
        console.log(gcode.length, name);
        this.writeCode('file_start', `${gcode.length} 0:/user/${name}`);
        let crc: number;
        let packet: Buffer;

        for (let i = 0; i < gcode.length + this.chunksize; i += this.chunksize) {
            packet = gcode.slice(i, i + this.chunksize);
            crc = crc32(packet);

            packet = Buffer.from(packet.toString().padEnd(this.chunksize, '\0'))

            const buf = Buffer.alloc(packet.length + 16);
            buf.writeUInt16LE(0x5a, 0);
            buf.writeUInt16LE(0x5a, 1);
            buf.writeUInt16LE(0xa5, 2);
            buf.writeUInt16LE(0xa5, 3);

            buf.writeUInt32BE(i, 4);
            buf.writeUInt32BE(packet.length, 8);
            buf.writeUInt32BE(crc, 12);

            for (let j = 0; j < packet.length; j++) {
                buf.writeUInt8(packet[j], 16 + j);
            }

            await this.writeAsync(buf, () => {
                console.log(`Sent ${i}/${gcode.length}`);
            });
        }

        this.socket.write('~M29\r\n', () => {
            console.log('sent M29');
        });
        console.log('done');
    }

    connect(host: string, port: number) {
        this.conopt.host = host;
        this.conopt.port = port;
        this.socket.connect(this.conopt);
    }

    writeAsync(data: Buffer, cb?: () => void) {
        return new Promise<void>((resolve) => {
            this.socket.write(data, () => {
                resolve();
                cb?.();
            });
        });
    }
}

export default Connection;

I'm getting this in the response:

    'N000541406256 error.\r\n' +
    'N000825634358 error.\r\n' +
    'N000924866865 error.\r\n' +
    'N000906643249 error.\r\n' +
    'N000824203310 error.\r\n' +
    'N0001161049648 error.\r\n' +
    'N000824203313 error.\r\n' +
    'N000825439008 error.\r\n' +
    'N0001479683640 error.\r\n' +
    'N000172437792 error.\r\n' +
    'N000542650674 error.\r\n' +
    'N000942874693 error.\r\n' +
    'N000825437744 error.\r\n' +
    'N0001969513823 error.\r\n' +
    'N000958421297 error.\r\n'

@estraco
Copy link
Author

estraco commented Jan 5, 2022

I FINALLY GOT IT!!! I don't know what my issue was, but this is the code that made it work:

import { crc32 } from 'crc';

function prepareFile(gcode: string, size: number): Buffer[] {
    const chunks = chunkArrayElements([...Buffer.from(gcode)], size)
        .filter(chunk => chunk.length > 0)
        .map(a => Buffer.from(a))
    const packets = chunks.map((chunk, index) => {
        console.log(index, chunk.length);
        const packet = Buffer.alloc(size + 16);
        packet.writeUInt16LE(0x5a, 0);
        packet.writeUInt16LE(0x5a, 1);
        packet.writeUInt16LE(0xa5, 2);
        packet.writeUInt16LE(0xa5, 3);

        packet.writeUInt32BE(index, 4);
        packet.writeUInt32BE(chunk.length, 8);
        packet.writeUInt32BE(crc32(chunk), 12);

        chunk.copy(packet, 16);

        return packet;
    });

    packets.push(Buffer.from('~M29\r\n'));

    return packets;
}

function chunkArraySize(array: any[], size: number): any[] {
    const results = Array(size);
    for (let i = 0; i < array.length; i++) {
        results[Math.floor(size / i)] = array[i];
    }
    return results;
}

function chunkArrayElements<T = any>(array: T[], chunkSize: number): T[][] {
    const results = [];
    for (let i = 0; ; i += chunkSize) {
        results.push(array.slice(i, i + chunkSize));
        if (i >= array.length) break;
    }
    return results;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants