Brother MFC-9320CW No Ethernet Link Repair

After the lightning strike on our mains transformer one of the printers (Brother MFC-9320CW) lost Ethernet link.

Upon further troubleshooting I concluded that the PHY chip gave up the ghost.
The chip in question is ASIX AX88796BLF. It appears from the board it has absolutely no input protection what so ever, hence it gave up the ghost.
A bit of warning: the AX88796BLF comes in QFP64 7mm x 7mm package with a microscopic 0.4mm pin pitch. I would not recommend this repair without hot air.

Continue reading Brother MFC-9320CW No Ethernet Link Repair

Converting DeWalt DCB101 110V charger to 240V

Here is how I converted north American Dewalt DCB101 to proper supply voltage (warning: do it at your own risk).
I have made this conversion based on many internet forum posts and youtube videos.
Note: there are at least two versions of this charger and newer version requires rewinding the transformer (at least according to internet).

Why? For some stupid reason 240V chargers locally cost 2 to 3 times more than in USA.

Continue reading Converting DeWalt DCB101 110V charger to 240V

Hate ads on your smart phone? Pi-hole it permanently!

Sorry about spammy headline ;).

Below I will describe how I got rid of ads on my android phone without rooting it.
It is very easy to get rid of ads on an android smart phone if you have root access. Unfortunately pesky manufacturers insist on declining warranty if the phone is rooted.
I will probably one day test this is in small claims court for a cheaper phone. I digress…

The requirements:

  • Some linux box/container/VM. In this post I used Ubuntu 16.04 LTS
  • Public IP on the box from above
  • Some linux/cli/unix experience

How does it work?

  1. Smart phone connects to the Linux box via OpenVPN
  2. Linux box is running Pi-hole which acts as a selective DNS server
  3. ???
  4. Proft!

Continue reading Hate ads on your smart phone? Pi-hole it permanently!

Simple IMAP to IMAP migration/sync tool

I had a need for a simple IMAP to IMAP sync tool, yet the only useful things I could find were offlineimap and imapsync.
The offlineimap is too complex and does not exactly do what I want; while the latter went commercial and not is not a clean install (requires messing with CPAN/perl libraries).

How hard can it be to write one?

Here it is:

The configuration is fairly simple (and self explanatory):

trash=Deleted Items


Here how it works:

It logs in into both IMAP servers and basically copies (with optional source deletion) the messages across. It also avoids duplication by checking Message-ID header. It should be stable enough to “daemon”-ify.

Here is a systemd unit for it (if one wants it to run all the time):


Description=IMAP to IMAP sync tool



GT06E GPS Tracker Part 1: Implementing communication protocol – WTF is CRC-ITU???


As an experiment I bought a relatively cheap GPS tracker that supported 3G (most of them at the time were 2G only).
After quick google search I found a suitable model GT06E from Concox.

The idea was I would implement my own server, as I do not trust 3rd party GPS tracking services (who would?), especially free ones ;).

I did not realise at the time what a mess the protocol is.
The “engineers” who wrote the spec for the protocol are crazy! They reinvented the wheel, which instead of tyre utilizes boots.

Continue reading GT06E GPS Tracker Part 1: Implementing communication protocol – WTF is CRC-ITU???

Beware of fake Philips HID Bulbs!

I decided to replace the 6300k HID bulbs on one of my cars, with more sensible 4300k OEM solution.

I came across reasonably priced Toyota Genuine Bulbs on (NZ ebay type of thing).

The particular bulbs I was after were D4R, or Toyota Genuine Part 90981-20015 (alternatively Philips 42406).

They were priced (~$80NZD) similar to Genuine Philips 42406 in USA (~$50USD), so seemed to be reasonable. The Toyota Genuine are after all Philips 42406 in TGP box.

When I tried to fit the bulbs I noticed they were extremely tight. Then I looked closely and did some googling.

Continue reading Beware of fake Philips HID Bulbs!

HUSQVARNA rip-off pricing in NZ

So, I came across an interesting, but not surprising thing with Husqvarna MSRP prices in NZ compared to USA.

I recently looked at Husqvarna 445 and when googled for it, I got multiple MSRP prices (one for NZ, on for AU and another for USA).

Husqvarna 445 MSRP by country:
USA: 329.95 USD

AU: 899.00 AUD (~$690 USD)

NZ: 1019.00 NZD (~$745 USD)

According to NZ customs duty calculator using US MSRP price as base the fair MSRP price in NZ should have been $599 NZD (~$440 USD).

What is with the ~$150 USD price difference between NZ and Australia? According to Husqvarna Americans are twice better than Australians and more than twice better than Kiwis. They treat Kiwis like chumps that will buy thing at whatever prices Husqvarna feels like.

Guess what, from now on I will be avoiding the Husqvarna until these greedy corporate types will pull their heads out of their asses and set fair MSRP prices. In this global economy it is very stupid not to have standardised prices across the globe.

If I really wanted to buy one, no way in hell I would be buying it in NZ, as for about $150NZ I can get it shipped from the states.

python OpenCV basic motion detection

Here I will describe how I use OpenCV for capturing RTSP streams, with purpose of motion detection.

For basic OpenCV I use these two libraries:

import cv2
import numpy as np

cv2 is OpenCV library (second version), and numpy is python numeric lybrary (useful for manipulating matrices among other things).

To initiate capture one simply does following:

cap = cv2.VideoCapture('rtsp://')

In this example I use second stream (of lower resolution) for motion detection.

From there you can get heigh and width of the frame (this will be useful later):

width = cap.get(3)
height = cap.get(4)

I use BackgroundSubtractorMOG for motion detection (somewhat cheating ;)):

bg = cv2.BackgroundSubtractorMOG(100,3,0.6,30)

The magic is in parameters, I used following:
100 – history
3 – number of Gaussian mixtures
0.6 – background ratio
30 – noise strength
The numbers above are not necessarily “correct” but I came to them with error and trial (and “guestimation”).
Here is document in detail describing this algorithm:

The actual capture loop looks something like this:

    ret, frame =
    motion = bg.apply(frame, learningRate=0.005)
    kernel = np.ones((3, 3), np.uint8)
    motion = cv2.morphologyEx(motion, cv2.MORPH_CLOSE, kernel, iterations=1)
    motion = cv2.morphologyEx(motion, cv2.MORPH_OPEN, kernel, iterations=1)
    motion = cv2.dilate(motion,kernel,iterations = 1)
    contours, hierarchy = cv2.findContours(motion, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
ret, frame =

captures a single frame

motion = bg.apply(frame, learningRate=0.005)

extracts a black and white image with the background removed (learnignRate value has been chosed by error and trial).

Next four lines simply manipulate extracted image in such that it does following:
MORPH_CLOSE: removes small holes (up to 3×3 pixel, defined by kernel) within the object (“white”) in the extracted motion matrix.
MORPH_OPEN: removes small dots within the “background” (black) in the extracted motion matrix.
dilate: is making sure there all adjacent islands are joined together, so when we extract contours we get small amount of contours as result.

The “3×3 pixel” block comes from here:

kernel = np.ones((3, 3), np.uint8

The last step from processing frame is extracting the contours:

contours, hierarchy = cv2.findContours(motion, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

The extracted contours can then be iterated and hull drawn around them:

for cnt in contours:
    hull = cv2.convexHull(cnt)

The hull points then can be checked against the mask if motion is inside of the area of interest:

Lets define the mask as the whole frame (I am pretty sure there is a better way;)):

mask_points = [
( 0 , 0 ),
( 1 , 0 ),
( 1 , 1 ),
( 0 , 1 ),

for point in mask_points:
    mask_array.append([[int(point[0] * width ), int(point[1] * height )]])
mask = np.array(mask_array, np.int32)

This looks cumbersome, but what I am achieving here is converting mask_points list of human readable relative coordinate tuples (eg: centre will be at (0.5,0.5)). Mask can be defined as a polygon with relative positioning of each corner to the frame (independent from pixel size).

We check if hull point is inside our mask

for point in hull:
    distance = cv2.pointPolygonTest(mask,tuple(point[0]),1)
    if distance > 0:

and vice-versa (in case if mask is smaller than the frame):

for point in mask:
    distance = cv2.pointPolygonTest(hull,tuple(point[0]),1)
    if distance > 0:

The above will tell if the motion contour extracted is within of area interest.
In addition to the checking if the motion happens withing of area of interest the severity/size of motion can be calculated by calculating the area of the hull via the following:

area += cv2.contourArea(hull)

which then can be compared with the total area:

surface = cv2.contourArea(mask)

The ratio can be converted to a percentage value and thus be used to trigger the recording if the value is above certain threshold:

relative = area * 100.0 / surface

I use ffmpeg for actual recording (it is way more efficient than dumping frames from HD OpenCV capture). I simply launch an ffmpeg subprocess when motion is detected and send a SIGTERM when motion is over:

p=subprocess.Popen(record_cmd,shell=False) # motion start
p.terminate() # motion stop

Note: the ffmpeg will cleanly close the recording if it sent the SIGTERM (opposed to SIGKILL).

For debug and entertainment purposes the image could be displayed via following:

To draw contours and hulls:

for cnt in contours:
    hull = cv2.convexHull(cnt)
    cv2.drawContours(f, [cnt], 0, (0,255,0),1)

Note: The colour is defined by this tuple: (0,255,0)

Then do display the whole thing insert this inside of the while(True):

k = cv2.waitKey(30) & 0xff
if k == 27:

The above is basic idea behind my motion detection scripts. I have omitted a lot of glue logic and arithmetic due to my script is not ready for public display ;).