AnswerBun.com

Arduino EPROM dumper with utility program in C

Arduino Asked on December 30, 2021

I am writing EPROM dumper with Arduino UNO and utility program in C and I am using Ubuntu 20.04. The Arduino sketch works fine, but I am having few problems with the utility program. The first problem is that the utility hangs (like in some kind of infinite loop) if I don’t open the serial monitor in Arduino IDE previously, or any other serial communication app (like puTTY). The second problem I have is that the utility also hangs when reading the last bytes which Arduino sends – inspecting the dumped file in hex editor, the last 4-6 bytes are missing. By printing string messages in the function serial_port_read_block_file, I think that the program hangs up in the read function.

Thank you in advance and sorry if I understood something wrong configuring the serial communication.

EDIT: Adding switch-case statement for setting the baud rate solved the first problem. The second problem is that I forgot that byte with the value of ‘n’ can be received, and I was just discarding this byte, so at the end I was missing few bytes. Also, I should have checked in the serial_port_read_block_file function if n>0, so I don’t access address beyond the scope of the block array (with the statement buffer_cur_pos[-1], if n=0 I accessed invalid address).

Conclusion: As @Majenko suggested, I should implement some protocol that will wrap up the data transfer, to distinguish the data from the start/stop conditions. I also changed the code below with the fixes which made the program working (both, in the Arduino sketch and the utility program).

The Arduino sketch:

//74HC595 pins
const int shiftData = 10;
const int shiftClock = 11;
const int shiftLatch = 12;

//ROM data pins (successive)
const int romD0 = 2;

//pgm constants and variables
static const uint16_t BLOCK_SIZE = 256;
uint16_t nBytes = 0;
boolean readMode = false;

void sendAddress(uint16_t addr) {
  //first, send the high byte of the address
  shiftOut(shiftData, shiftClock, MSBFIRST, addr >> 8);
  //then, send the low byte of the address
  shiftOut(shiftData, shiftClock, MSBFIRST, addr);
  //pulse the shiftLatch pin, to output the address
  digitalWrite(shiftLatch, LOW);
  digitalWrite(shiftLatch, HIGH);
  digitalWrite(shiftLatch, LOW);
}

uint8_t readByte(uint16_t addr) {
  uint8_t data = 0;
  //send the address
  sendAddress(addr);
  for (int i = 7; i >= 0; i--)
    data = (data << 1) | digitalRead(romD0 + i);
  return data;
}

void sendBytes(uint16_t bytes) {
  //send the data in blocks with size BLOCK_SIZE
  const uint16_t BLOCKS = bytes / BLOCK_SIZE; //number of whole blocks
  const uint16_t REMAINDER = bytes % BLOCK_SIZE; //the remaining bytes that don't form a block

  uint8_t data[BLOCK_SIZE]; //buffer for block of bytes to be sent

  //send the whole blocks
  for (uint16_t block = 0; block < BLOCKS; block++) {
    for (uint16_t offset = 0; offset < BLOCK_SIZE; offset++) {
      data[offset] = readByte(block * BLOCK_SIZE + offset);
    }
    Serial.write(data, BLOCK_SIZE);
    delay(100);
  }

  //send the remaining bytes
  for (uint16_t offset = 0; offset < REMAINDER; offset++) {
    data[offset] = readByte(BLOCKS * BLOCK_SIZE + offset);
  }
  Serial.write(data, REMAINDER);
  Serial.write("rn"); //end of transmission
  delay(100);
}

void setup() {
  pinMode(shiftData, OUTPUT);
  pinMode(shiftClock, OUTPUT);
  pinMode(shiftLatch, OUTPUT);

  for (int i = 0; i < 8; i++)
    pinMode(romD0 + i, INPUT);

  Serial.begin(57600);

  //wait for serial port to connect
  while (!Serial) {}
}

void loop() {
  while (!Serial.available()) {} //wait until data is available
  char c = Serial.read();
  delay(100);
  switch (c) {
    case 'S': //send ready command
      Serial.write("> ");
    break;
    case 'R':
      readMode = true;
      while ((c = Serial.read()) != 'n') {
        nBytes = nBytes * 10 + (c - '0');
      }
      delay(100);
    break;
    default:
    break;
  }

  if (readMode) {
    sendBytes(nBytes);
  }

  //reset for next loop
  readMode = false;
  nBytes = 0;
  delay(100);
}

The utility program:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

static const uint16_t BLOCK_SIZE = 256;
static const uint32_t BAUD_RATE = 57600;
    
void help(void) {
    printf(
            "rdump - ROM dumpern"
            "usage: rdump [OPTIONS]n"
            "Options:n"
            "-p <serial port>       specify serial port of the Arduinon"
            "-f <filename>          specify output filenamen"
            "-s <num bytes>         specify the number of bytes to readn"
            "-h                     print this help filen"
    );
}

//opening and initializing serial communication
int serial_port_init(const char *port, uint32_t baud) {

    //open serial port for read/write, save its file descriptor in fd
    int fd = open(port, O_RDWR | O_NOCTTY | O_SYNC);
    
    if(fd == -1) {
        fprintf(stderr, "serial_port_init: unable to open %sn", port);
        return -1;
    }
    
    struct termios options; //serial port options
    
    //get the initial serial port options
    if(tcgetattr(fd, &options) < 0) {
        fprintf(stderr, "serial_port_init: unable to get terminal optionsn");
        return -1;
    }
    
    //set the baud rate
    cfsetispeed(&options, (speed_t)baud);
    cfsetospeed(&options, (speed_t)baud);
    
    options.c_cflag |= (CLOCAL | CREAD); //enable the receiver and block control lines
    
    //set the character size to 8 bits, no parity bit and 1 stop bit (8N1 configuration)
    options.c_cflag &= ~PARENB; //no parity
    options.c_cflag &= ~CSTOPB; //1 stop bit
    options.c_cflag &= ~CSIZE; //bit mask for character size
    options.c_cflag |= CS8; //8 bit character size
    //disable hardware flow control
    options.c_cflag &= ~CRTSCTS;
    //configure for non-canonical mode
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG); //disable cannonical mode, echo, erasure, new-line echo,  INTR, QUIT and SUSP
    options.c_iflag &= ~(IXON | IXOFF | IXANY); //disable software flow control
    options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); //disable any special handling of received bytes
    options.c_oflag &= ~OPOST; // prevent special interpretation of output bytes
    options.c_oflag &= ~ONLCR; // prevent conversion of newline to carriage return/line feed
    
    //read returns after 0.5 second timeout
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 5;
    
    serial_port_flush(fd); //flush the serial port
    
    //set the new options for the serial port
    if(tcsetattr(fd, TCSANOW, &options) < 0) {
        fprintf(stderr, "serial_port_init: unable to set terminal optionsn");
        return -1;
    }
    
    return fd;
}

//closing the serial communication
void serial_port_close(int fd) {
    close(fd);
}

void serial_port_flush(int fd) {
    sleep(2);
    tcflush(fd, TCIOFLUSH);
}

//check if serial port is ready
int serial_port_ready(int fd) {
    uint8_t c[1];
    uint8_t prev = 0;
    ssize_t n = read(fd, c, 1);
    int done = 0;
    if(n <= 0) return 0;
    prev = c[0];
    do {
        //printf("%02X", (uint8_t)prev);
        n = read(fd, c, 1);
        if(n <= 0) return 0;
        done = (prev == '>') && (c[0] == ' ');
        prev = c[0];
    } while(!done);
    return 1;
}

//writing to the serial port
ssize_t serial_port_write(int fd, const char *data) {
    size_t data_length = strlen(data);
    ssize_t n = write(fd, data, data_length);
    if(n != data_length) {
        fprintf(stderr, "serial_port_write: unable to write the datan");
        return -1;
    }
    return n;
}

//reading block of bytes from the serial port into file
int32_t serial_port_read_block_file(int fd, uint8_t *block, uint16_t block_size, FILE **file) {
    uint8_t *block_cur_pos = block;
    memset(block, 0, block_size);
    
    ssize_t n;
    uint16_t total = 0;
    int done = 0;
    int try = 0;
    static const int TRIES = 5;
    //printf("serial_port_read_block_file: started reading block.n");
    do {
        n = read(fd, block_cur_pos, block_size - (block_cur_pos - block));
        if(n == -1) {
            fprintf(stderr, "serial_port_read_block_file: unable to read the datan");
            return -1;
        }
        block_cur_pos += n;
        total += n;
        //print_byte_array(block, block_cur_pos - block + 1);
        if(n > 0) {
            done = (block_cur_pos[-1] == 'n' && block_cur_pos[-2] == 'r') || (total == block_size);
        } else {
            try++;
            if(try >= TRIES) done = 1;
        }
    } while(!done);
    //printf("serial_port_read_block_file: done reading the required block. Total read: %u bytesn", total);
    if(total == 0) return 0;
    if(block_cur_pos[-1] == 'n' && block_cur_pos[-2] == 'r') {
        total -= 2;
        fwrite(block, sizeof(uint8_t), total, *file);
    } else {
        fwrite(block, sizeof(uint8_t), total, *file);
    }
    fflush(*file);
    //printf("serial_port_read_block_file: done writing the bytes to the file.n");
    return total;
}

//reading (bytes) bytes from the serial port into file
int32_t serial_port_read_file(int fd, FILE **file, uint16_t bytes) {
    uint8_t *block = (uint8_t*)malloc(BLOCK_SIZE * sizeof(uint8_t));
    uint16_t total = 0;
    ssize_t n;
    do {
        n = serial_port_read_block_file(fd, block, BLOCK_SIZE, file);
        if(n == -1) {
            return -1;
        }
        total += n;
    } while(total < bytes);
    free(block);
    //printf("Done reading.......n");
    return total;
}

int main(int argc, char *argv[]) {
    char *port_name = NULL;
    char *file_name = NULL;
    uint16_t bytes;
    //parsing command line arguments
    opterr = 0;
    char c;
    while((c = getopt(argc, argv, "p:f:s:h")) != -1) {
        switch(c) {
            case 'p':
                port_name = strdup(optarg);
            break;
            case 'f':
                file_name = strdup(optarg);
            break;
            case 's':
                bytes = atoi(optarg);
            break;
            case 'h':
                help();
                return 0;
            break;
            default:
                fprintf(stderr, 
                        "rdump: invalid option: %cn"
                        "Try 'rdump -h' for more information.n", 
                        optopt);
                return 0;
            break;
        }
    }
    
    //opening serial port communication with the arduino
    int port = serial_port_init(port_name, BAUD_RATE);
    if(port < 0) {
        //fprintf(stderr, "rdump: unable to open port %sn", port_name);
        free(port_name);
        free(file_name);
        return -1;
    }
    
    printf("rdump: serial communication to %s opened successfullyn", port_name);
    sleep(3); //wait for serial port to be ready

    //send command for testing if the serial port is ready
    char cmd[20];
    cmd[0] = 'S';
    cmd[1] = '';
    if(serial_port_write(port, cmd) < 0) {
        serial_port_close(port);
        return -3;
    }
    //check if serial port is ready
    if(!serial_port_ready(port)) {
        fprintf(stderr, "rdump: serial port is not ready.n");
        serial_port_close(port);
        free(port_name);
        free(file_name);
        return -2;
    }
    printf("rdump: serial port is readyn");
    //send command for reading n bytes
    sprintf(cmd, "R%dn", bytes);
    if(serial_port_write(port, cmd) < 0) {
        serial_port_close(port);
        free(port_name);
        free(file_name);
        return -3;
    }
    //receive the requested data
    FILE *rom = fopen(file_name, "wb");
    fsync(fileno(rom));
    if(rom == NULL) {
        fprintf(stderr, "rdump: unable to create file %sn", file_name);
        serial_port_close(port);
        free(port_name);
        free(file_name);
        return -4;
    }
    printf("rdump: file %s created successfully.n", file_name);
    printf("Dumping ROM to file %s...n", file_name);
    //uint8_t buffer[10000];
    //int n = serial_port_read_block_file(port, buffer, 10000, rom);
    //int n = serial_port_read(port, buffer, 10000);
    //fwrite(buffer, sizeof(uint8_t), n, rom);
    ssize_t dumped_bytes = serial_port_read_file(port, &rom, bytes);
    if(dumped_bytes < 0) {
        return -5;
    }
    if(dumped_bytes != bytes) {
        fprintf(stderr, "Dumped %zd of %u bytesn", dumped_bytes, bytes);
    } else {
        printf("Done!n");
    }
    //close the file, serial port and free the memory
    fclose(rom);
    serial_port_close(port);
    free(port_name);
    free(file_name);
    //printf("Exiting...n");
    return 0;
}

Add your own answers!

Related Questions

Strange Arduino Serial Behavior

1  Asked on September 25, 2021 by interrobang

     

NodeMCU ESP8266

1  Asked on September 25, 2021 by anirudh-garg

     

Arduino Due Master Programming

0  Asked on September 25, 2021 by vamshi-krishna

       

Arduino Modbus Slave Library Needed

1  Asked on September 25, 2021 by a-h-shukry

 

2xArduinos, 2xDevices, I2C setup

0  Asked on September 25, 2021 by tomasito

     

ESP8266 OTA update with different flash size settings?

1  Asked on September 25, 2021 by brick

       

Esp32: is OTA compatible with FREERTOS

1  Asked on September 25, 2021 by glamis

   

regarding ardunio error

0  Asked on September 25, 2021 by pratham-maheshwari

     

ESP12E motorshield analogWrite issue when powering DC motor

1  Asked on February 28, 2021 by ameya-savale

     

Arduino Turn Signals

2  Asked on February 22, 2021

 

Arduino Pro Micro bricked?

2  Asked on February 17, 2021 by george-sp

     

Ask a Question

Get help from others!

© 2022 AnswerBun.com. All rights reserved. Sites we Love: PCI Database, MenuIva, UKBizDB, Menu Kuliner, Sharing RPP, SolveDir