Updating Brother printer firmware (part 2)

•May 26, 2014 • Leave a Comment

Based on my previous post, I’ve received few enquiries regarding updating different network printers.
In the SNMP walk, you should find a section with lines; MODEL, SERIAL, SPEC (optional) and FIRMID (multiple if there are more than one firmware types). If the printer is in maintenance mode then the required lines will not be present in the SNMP output, and sometime is not there at all!

Here is a list of printers which I’ve been able to retrieve.

NAME SPEC FIRMID
DCP-1510 series 0003 MAIN,SUB3
DCP-1608 0020 MAIN
DCP-1610W series 0017 MAIN
DCP-7030 0101 MAIN
DCP-7040 0001 MAIN
DCP-7055 0001 MAIN
DCP-7055W 0001 MAIN
DCP-7060D 0001 MAIN,SUB1
DCP-7065DN 3004 MAIN,SUB1
DCP-7070DW 0001 MAIN,PCLPS,SUB1
DCP-7080 0020 MAIN
DCP-7080D 0120 MAIN
DCP-8070D 0024 MAIN,PCLPS
DCP-8080DN 0001 MAIN,PCLPS
DCP-8085DN 0101 MAIN,PCLPS
HL-1110 series MAIN
HL-1200 series MAIN
HL-1208 MAIN
HL-2220 series MAIN
HL-2230 series MAIN
HL-2240 series MAIN
HL-2240D series MAIN
HL-2250DN series MAIN
HL-2270DW series MAIN
HL-3140CW series MAIN,PCLPS,SUB1
HL-3150CDW series MAIN,SUB1
HL-3170CDW series MAIN,PCLPS,SUB1
HL-5250DN series MAIN,BRNET,BRNET2
HL-5270DN series MAIN,BRNET,BRNET2
HL-5280DW series MAIN,BRNET
HL-5370DW series MAIN,BRNET
HL-5440D series 0001 MAIN
HL-5450DN series 0004 MAIN,SUB1
HL-5470DW series 0101 MAIN,SUB1
HL-S7000DN series MAIN,ENGINE,SUB1
MFC-1810 series 0003 MAIN,SUB3
MFC-1900 series 0019 MAIN
MFC-1906 0020 MAIN
MFC-1910W series 0003 MAIN
MFC-1919NW 0020 MAIN
MFC-7340 0002 MAIN
MFC-7360N 2048 MAIN,SUB1
MFC-7460DN 0001 MAIN,SUB1,IFAX
MFC-7470D MAIN,SUB1,PCLPS
MFC-7840W 0002 MAIN,IFAX
MFC-7860DN 0001 MAIN,PCLPS,SUB1,IFAX
MFC-7860DW 2248 MAIN,PCLPS,SUB1,IFAX
MFC-8370DN 2148 MAIN,PCLPS,IFAX
MFC-8520DN 0248 MAIN,SUB1,IFAX
MFC-8880DN 2348 MAIN,PCLPS
MFC-8890DW 0401 MAIN,PCLPS
MFC-9320CW 0201 MAIN,PCLPS,IFAX
MFC-9340CDW 0502 MAIN,SUB1,SUB2,IFAX
MFC-9970CDW MAIN,PCLPS,SUB2
MFC-L8600CDW MAIN,SUB1,SUB2,SUB4,IFAX

The following script performs the required HTTP requests.

#!/bin/bash
  
name=
spec=
firms=
  
while getopts ":n:s:f:" opt; do
  case $opt in
    n)
      name=$OPTARG
      ;;
    s)
      spec=$OPTARG
      ;;
    f)
      firms=$OPTARG
      ;;
  esac
done
  
IFS=',' read -a firmids <<< "$firms"
for firmid in "${firmids[@]}"
do
    for version in "0" "B0000000000"
    do
        echo -n '<REQUESTINFO><FIRMUPDATETOOLINFO><FIRMCATEGORY>' >> request.xml
        echo -n $firmid >> request.xml
        echo -n '</FIRMCATEGORY><OS>LINUX</OS><INSPECTMODE>1</INSPECTMODE></FIRMUPDATETOOLINFO><FIRMUPDATEINFO><MODELINFO><NAME>' >> request.xml
        echo -n $name >> request.xml
        echo -n '</NAME><SPEC>' >> request.xml
        echo -n $spec >> request.xml
        echo -n '</SPEC><DRIVER></DRIVER><FIRMINFO><FIRM><ID>' >> request.xml
        echo -n $firmid >> request.xml
        echo -n '</ID><VERSION>' >> request.xml
        echo -n $version >> request.xml
        echo -n '</VERSION></FIRM></FIRMINFO></MODELINFO><DRIVERCNT>1</DRIVERCNT><LOGNO>2</LOGNO><ERRBIT></ERRBIT><NEEDRESPONSE>1</NEEDRESPONSE></FIRMUPDATEINFO></REQUESTINFO>' >> request.xml
  
        curl -X POST -d @request.xml https://firmverup.brother.co.jp/kne_bh7_update_nt_ssl/ifax2.asmx/fileUpdate -H "Content-Type:text/xml" --sslv3 -o response.xml -s
        valid=`grep "<RESPONSEINFO><FIRMUPDATEINFO><VERSIONCHECK>0</VERSIONCHECK>" response.xml`
        if [ -n "$valid" ]
        then
            echo "--- request (name=$name, spec=$spec, firm=$firmid, version=$version) ---"
            cat request.xml | xmllint --format -
            echo "--- response ---"
            cat response.xml | xmllint --format -
        else
            partial=`grep "<RESPONSEINFO><FIRMUPDATEINFO><VERSIONCHECK>2</VERSIONCHECK><FIRMID>" response.xml`
            if [ -n "$partial" ]
            then
                echo "-- the request is partially incomplete (name=$name, spec=$spec, firm=$firmid, version=$version) --"
            else
                echo "-- the request is invalid (name=$name, spec=$spec, firm=$firmid, version=$version) --"
            fi
        fi
        rm request.xml response.xml
    done
done

Here is an example to fetch both firmwares for the HL-5450DN.

./fetch.sh -n "HL-5450DN series" -s 0004 -f MAIN,SUB1

*** If you have successfully retrieved the correct info from the SNMP walk; then please let me know and I’ll update this post with your printer.

Updating Brother printer firmware with Linux

•February 5, 2014 • 1 Comment

I’ve got a network laser printer (Brother DCP-7065DN) in my home office which is only accessible from a Linux desktop …and guest what?; you can only update the firmware from a M$ Windows PC or from a Mac! 😦

After a lot of Googling and talking to the Brother’s support (which is obviously clueless), I’ve found the following blogs:

Resurrecting a Brother HL-2250DN after a failed firmware update
Brother Network Firmware Update with Linux

To get the content of the WebService request, I’ve downloaded a MIB SNMP browser from iReasoning and done a SNMP walk to extract the required values. The printer came with an original firmware version D (22/10/2010) and the latest is version J (06/09/2013).

<REQUESTINFO>
    <FIRMUPDATETOOLINFO>
        <FIRMCATEGORY>MAIN</FIRMCATEGORY>
        <OS>LINUX</OS>
        <INSPECTMODE>1</INSPECTMODE>
    </FIRMUPDATETOOLINFO>

    <FIRMUPDATEINFO>
        <MODELINFO>
            <SELIALNO>E69758E1N499775</SELIALNO>
            <NAME>DCP-7065DN</NAME>
            <SPEC>3004</SPEC>
            <DRIVER></DRIVER>
            <FIRMINFO>
                <FIRM>
                    <ID>MAIN</ID>
                    <VERSION>D1104201307:1B1C</VERSION>
                </FIRM>
            </FIRMINFO>
        </MODELINFO>
        <DRIVERCNT>1</DRIVERCNT>
        <LOGNO>2</LOGNO>
        <ERRBIT></ERRBIT>
        <NEEDRESPONSE>1</NEEDRESPONSE>
    </FIRMUPDATEINFO>
</REQUESTINFO>

…after curling the request to the WebService

curl -X POST -d @request.xml https://firmverup.brother.co.jp/kne_bh7_update_nt_ssl/ifax2.asmx/fileUpdate -H "Content-Type:text/xml" --sslv3

…BOOM!! 😉

<?xml version="1.0" encoding="UTF-8" ?>
<RESPONSEINFO>
  <FIRMUPDATEINFO>
    <VERSIONCHECK>0</VERSIONCHECK>
    <MEMORYVERSION>a</MEMORYVERSION>
    <FIRMID>MAIN</FIRMID>
    <LATESTVERSION>J1307022123</LATESTVERSION>
    <PATH>http://update-akamai.brother.co.jp/CS/LZ3637_J.djf</PATH>
    <DLTIME>129000</DLTIME>
  </FIRMUPDATEINFO>
</RESPONSEINFO>

…finally just need to flash the firmware via FTP

wget http://update-akamai.brother.co.jp/CS/LZ3637_J.djf
ftp <printer IP address> (the user id is the admin password!)
  bin
  hash
  send LZ3637_J.djf
  bye

I can’t believe how easy it’ll be to provide a cross-platform tool to flash the firmware or just providing a direct link to the firmware file! …come on @Brother!!

Adapting Saturation with ImageMagick

•June 1, 2010 • Leave a Comment

In the previous article on Enfuse, the colours are a little dull on the tone mapped output image. So I’ve found a tutorial [1] which apply an intensity of saturation based on the existent saturated/unsaturated pixels.

#tested with ImageMagick 6.6.1-10
convert ${min_ev_name}_tm.tiff -sigmoidal-contrast 2x50% -alpha off ${min_ev_name}_contrast.tiff

convert -set option:modulate:colorspace HSB \( ${min_ev_name}_contrast.tiff -modulate 100,130,100 \) \( -clone 0 \( -clone 0 -modulate 100,0,100 \) -compose difference -composite -auto-level -negate \) -compose CopyOpacity -composite ${min_ev_name}_saturation_mask.tiff

composite ${min_ev_name}_saturation_mask.tiff ${min_ev_name}_contrast.tiff ${min_ev_name}_final.tiff
tone mapping with Enfuse adapting saturation mask 30% adapting saturation



Here is a comparison between a standard 30% saturation and the adapting saturation. The white colour is the full 30% saturation ratio and the gray colour is the area where the adapting technique is applied.
saturation difference



A big thanks to ImageMagick [2] and Meet the Gimp [3] communities for the useful pointers!

[1] Adaptive Saturation
[2] ImageMagick – forum
[3] Meet the Gimp – forum

Tone Mapping with Enfuse

•December 1, 2009 • Leave a Comment

The previous article was about producing an image free of noise, now we can apply a tone mapping [1]. I’m going to use Enfuse to generate a natural HDR/LDR image.



Due to the nature of the image produced by the Noise Suppression technique, we can generate over-exposed images (1EV 2EV 3EV 4EV) without any quality issues. Then we are going to feed the resultant images (with a gamma 2.2 applied) to Enfuse.

convert ${min_ev_name}_fon.tiff -alpha off -gamma 2.2 ${min_ev_name}_tone_a.tiff
letters=( a b c d e f g h i j k l m n o p q r s t u v w x y z )
counter=1
while true; do
  exp=`echo "2^${counter}" | bc`
  if [ "${exp}" -gt "${max_exp}" ]; then
     break
  fi
  convert ${min_ev_name}_fon.tiff -alpha off -evaluate multiply ${exp} -gamma 2.2 ${min_ev_name}_tone_${letters[${counter}]}.tiff
  let counter=${counter}+1
done
convert ${min_ev_name}_fon.tiff -alpha off -evaluate multiply ${max_exp} -gamma 2.2 ${min_ev_name}_tone_${letters[${counter}]}.tiff
tone_files=`ls ${min_ev_name}_tone_*.tiff`
enfuse -v -o ${min_ev_name}_tm.tiff ${tone_files}



Enfuse generates internally the following merging masks.



Finally Enfuse produces a natural looking image.



Additionally we can process the image a little bit further by applying contrast and saturation to the HDR/LDR image. I’m using a sigmoidal contrast curve of 2×50% and a 30% increase of saturation.

convert ${min_ev_name}_tm.tiff -sigmoidal-contrast 2x50% -modulate 100,130,100 ${min_ev_name}_final.tiff




[1] Guillermo Lujik – HDR TONE MAPPING

Noise Suppression with ImageMagick

•December 1, 2009 • Leave a Comment

This post is a tribute to Guillermo Luijk‘s Zero Noise technique [1].

To replicate this process under Linux, I’m using dcraw and ImageMagick. To compare apple to apple, I’ve used the photos from Guillermo’s article.

The aim of this technique is to replace noisy pixels from the original image (0EV) with the pixels from the other over-exposed images. Therefore you need to take a number of catches with 2EV apart (e.g: 0EV +2EV +4EV) using a tripod. The reference image is the 0EV catch and that’s where the technique will apply.

The first step is to convert the raw files to linear TIFF (16bits) using the sRGB colorspace with for example an automatic white balance.

max_ev=`ls -1 *.cr2 | tail -n 1`
max_ev_name=`echo ${max_ev} | cut -d '.' -f1`
dcraw -v a -W -o 1 -q 3 -4 -T -c ${max_ev} > ${max_ev_name}.tiff 2> dcraw.log
cat dcraw.log
rgbg=`grep 'multipliers' dcraw.log | cut -d ' ' -f2,3,4,5`
rm dcraw.log
for other in $( ls -r *.cr2 ); do
  if [ ${other} != ${max_ev} ]; then
    dcraw -v -r $rgbg -W -o $color -q 3 -4 -T ${other}
  fi
done



Second step is to create the merging masks (2 masks for 3 catches). A mask is produced from the negate of over-exposed image which is copied into the alpha channel [2] of the corrected version of the over-exposed image. For example, the corrected version of the +2EV image is 0EV.
To generate the corrected images, I’ve wrote a little program (based on Guillermo’s algorithm) using the ImageMagick C++ API. The program prints the exposure difference between 2 images.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <wand/MagickWand.h>

int main(int argc,char **argv) {
  MagickBooleanType status;
  MagickPixelPacket pixel1, pixel2;
  MagickWand *image1, *image2;
  PixelIterator *iterator1, *iterator2;
  PixelWand **pixels1, **pixels2;

  long y;  
  register long x;
  unsigned long width;

  double min = 65536 / pow(2, 6);
  double max = 65536 * 0.9;
  double sum1 = 0, sum2 = 0;

  if (argc != 3) {
      fprintf(stdout, "Usage: %s normal-image over-exposed-image\n", argv[0]);
      exit(0);
  }

  MagickWandGenesis();
  image1 = NewMagickWand();
  status = MagickReadImage(image1, argv[1]);
  if (status == MagickFalse) {
    return -1;
  }
  image2 = NewMagickWand();
  status = MagickReadImage(image2, argv[2]);
  if (status == MagickFalse) {
    return -1;
  }

  iterator1 = NewPixelIterator(image1);
  iterator2 = NewPixelIterator(image2);
  if ((iterator1 == (PixelIterator *) NULL) || (iterator2 == (PixelIterator *) NULL)) {
    return -1;
  }
  for (y=0; y < (long) MagickGetImageHeight(image1); y++) {
    pixels1 = PixelGetNextIteratorRow(iterator1, &width);
    pixels2 = PixelGetNextIteratorRow(iterator2, &width);
    if ((pixels1 == (PixelWand **) NULL) || (pixels2 == (PixelWand **) NULL)) {
      break;
    }
    for (x=0; x < (long) width; x++) {
      PixelGetMagickColor(pixels1[x], &pixel1);
      PixelGetMagickColor(pixels2[x], &pixel2);
      if ((pixel1.red >= min) && (pixel1.red <= max) && (pixel2.red >= min) && (pixel2.red <= max)) {
         sum1 += pixel1.red;
         sum2 += pixel2.red;
      }
      if ((pixel1.green >= min) && (pixel1.green <= max) && (pixel2.green >= min) && (pixel2.green <= max)) {
         sum1 += pixel1.green;
         sum2 += pixel2.green;
      }
      if ((pixel1.blue >= min) && (pixel1.blue <= max) && (pixel2.blue >= min) && (pixel2.blue <= max)) {
         sum1 += pixel1.blue;
         sum2 += pixel2.blue;
      }
    }
  }
  if (y < (long) MagickGetImageHeight(image1)) {

    return -1;
  }
  iterator1 = DestroyPixelIterator(iterator1);
  image1 = DestroyMagickWand(image1);
  iterator2 = DestroyPixelIterator(iterator2);
  image2 = DestroyMagickWand(image2);
  MagickWandTerminus();

  printf("%.100g\n", sum1/sum2);
  return(0);
}
min_ev=`ls -1r *.tiff | tail -n 1`
for ev in $( ls *.tiff ); do
  if [ ${ev} != ${min_ev} ]; then
    ev_name=`echo ${ev} | cut -d '.' -f1`
    ec=`exposure ${min_ev} ${ev}`
    convert ${ev} -evaluate multiply $ec ${ev_name}_corrected.tiff
    composite -compose CopyOpacity \( ${ev} -negate \) ${ev_name}_corrected.tiff ${ev_name}_mask.tiff
  fi
done



To get the final image, we need to merge the masks together and overlay the mask to the first image.

mask_files=`ls -r *_mask.tiff`
nb_mask=`ls  -r *_mask.tiff | wc -l`
if [[ $( echo "$nb_mask == 1.00" | bc ) == "1" ]]; then
  mv $mask_files final_mask.tiff
else
  composite ${mask_files} final_mask.tiff
fi
min_ev_name=`echo ${min_ev} | cut -d '.' -f1`
composite final_mask.tiff ${min_ev} ${min_ev_name}_fon.tiff



Here is the final bash script which takes in argument a folder with a number of catches. With the -c option, you can compile the latest dcraw and the exposure program (you need the ImageMagick development package). The script requires the following tools: bash, cut, tail, grep, bc, wget, gcc and ImageMagick Q16.

#!/bin/bash
compile=
color=1
wb=-w
ext=dng

while getopts 'w:e:c' OPTION
  do
    case $OPTION in
      w)      wb=-"$OPTARG"
               ;;
      e)      ext="$OPTARG"
               ;;
      c)      compile=1
               ;;
      ?)      printf "Usage: %s: [-w {dcraw white balance: a|w}] [-e {file extension}] [-c compile] {folder}\n" $(basename $0) >&2
               exit 2
               ;;
      esac
      done
shift $(($OPTIND - 1))

if [ -z $1 ]; then
  printf "Usage: %s: [-w {dcraw white balance: a|w}] [-e {file extension}] [-c compile] {folder}\n" $(basename $0) >&2
  exit 2;
fi

if [ "$compile" ]; then
  echo "--- compile dcraw"
  wget http://cybercom.net/~dcoffin/dcraw/dcraw.c
  gcc -o dcraw -O4 dcraw.c -lm -ljpeg -llcms
  rm dcraw.c
  echo "--- compile exposure"
  gcc `Magick-config --cflags --cppflags` -o exposure exposure.c `Magick-config --ldflags --libs`
fi

echo "--- process folder=${1}"
cd ${1}
rm *.tiff

echo "--- convert raw to tiff"
max_ev=`ls -1 *.${ext} | tail -n 1`
max_ev_name=`echo ${max_ev} | cut -d '.' -f1`
../dcraw -v $wb -W -o $color -q 3 -4 -T -c ${max_ev} > ${max_ev_name}.tiff 2> dcraw.log
cat dcraw.log
rgbg=`grep 'multipliers' dcraw.log | cut -d ' ' -f2,3,4,5`
rm dcraw.log
for other in $( ls -r *.${ext} ); do
  if [ ${other} != ${max_ev} ]; then
    ../dcraw -v -r $rgbg -W -o $color -q 3 -4 -T ${other}
  fi
done

min_ev=`ls -1r *.tiff | tail -n 1`
max_exp=0
for ev in $( ls *.tiff ); do
  if [ ${ev} != ${min_ev} ]; then
    ev_name=`echo ${ev} | cut -d '.' -f1`
    echo -n "--- calculate negative exposure between ${min_ev} and ${ev}="
    ec=`../exposure ${min_ev} ${ev}`
    echo $ec
    max_exp=`echo "1/$ec" | bc`
    echo "--- create a mask for ${ev}"
    convert ${ev} -evaluate multiply $ec ${ev_name}_corrected.tiff
    composite -compose CopyOpacity \( ${ev} -negate \) ${ev_name}_corrected.tiff ${ev_name}_mask.tiff
  fi
done

echo "--- merge the masks"
mask_files=`ls -r *_mask.tiff`
nb_mask=`ls  -r *_mask.tiff | wc -l`
if [[ $( echo "$nb_mask == 1.00" | bc ) == "1" ]]; then
  mv $mask_files final_mask.tiff
else
  composite ${mask_files} final_mask.tiff
fi

echo "--- overlay the mask with ${min_ev}"
min_ev_name=`echo ${min_ev} | cut -d '.' -f1`
composite final_mask.tiff ${min_ev} ${min_ev_name}_fon.tiff

rm *_mask.tiff *_corrected.tiff




[1] Guillermo Lujik – ZERO NOISE
[2] ImageMagick – Copy Opacity