Mining Ethereum on M1 Mac GPU

TL;DR: It’s possible to mine Ethereum on a M1 Mac GPU. Hashrate is about 2Mh/s.

Mining on a M1 Mac

I’ve had my M1 MacBook Air for a bit of time now, and I also recently started mining Ethereum. I can’t help asking myself: What’s Ethereum mining performance like on a M1 Mac?

The obvious thing to do first is to run the off-the-shelf ethminer, which gives the following error:

ethminer 0.19.0-alpha.0
Build: darwin/release/appleclang

Unrecognized platform Apple
Error: No usable mining devices found

Not good. Apparently Apple GPUs are not whitelisted in ethminer. That should be easy to fix. Relevant lines are in libethash-cl/CLMiner.cpp, and I added Apple GPUs to the whitelist, pretending it’s an Intel GPU.

Then boost won’t compile since it’s trying to compile with a -fcoalesce-templates argument, which doesn’t exist in recent clang versions. So I have to update boost to the latest version, and fix relevant asio code since ethminer was using deprecated asio APIs.

I also need to upgrade OpenSSL to the latest version to have it support darwin + arm64.

After getting everything to compile. Here’s the result:

ethminer 0.19.0-17+commit.ce52c740.dirty
Build: darwin/release/appleclang

 i 19:51:36          Configured pool
 i 19:51:36          Selected pool
 i 19:51:36          Connection remotely closed by
 i 19:51:36          Stratum mode : EthereumStratum/1.0.0 (NiceHash)
 i 19:51:36          Established connection to []
 i 19:51:36          Spinning up miners...
cl 19:51:36 cl-0     Using Device : Intel GPU 0.0 Apple M1 OpenCL 1.2  Memory : 10.67 GB (11453251584 B)
 i 19:51:36          Extranonce set to 778d
 i 19:51:36          Extranonce set to 778d
 i 19:51:36          Authorized worker [REDACTED]
 i 19:51:36          Epoch : 397 Difficulty : 10.00 Gh
 i 19:51:36          Job: c7fc5311… []
cl 19:51:38 cl-0     Generating split DAG + Light (total): 4.10 GB
 i 19:51:38          Job: 40a57756… []
cl 19:51:38 cl-0     OpenCL kernel
cl 19:51:38 cl-0     Creating DAG buffer, size: 4.10 GB, free: 6.57 GB
cl 19:51:38 cl-0     Creating light cache buffer, size: 65.62 MB
cl 19:51:38 cl-0     Loading kernels
cl 19:51:38 cl-0     Creating buffer for header.
cl 19:51:38 cl-0     Creating mining buffer
 m 19:51:41          0:00 A0 0.00 h - cl0 0.00
 i 19:51:42          Job: 077b62f6… []
 m 19:51:46          0:00 A0 0.00 h - cl0 0.00
 i 19:51:46          Job: 2835839e… []
 m 19:51:51          0:00 A0 0.00 h - cl0 0.00
 m 19:51:56          0:00 A0 0.00 h - cl0 0.00
 i 19:51:57          Job: 97f724e7… []
 m 19:52:01          0:00 A0 0.00 h - cl0 0.00
 m 19:52:06          0:00 A0 0.00 h - cl0 0.00
 m 19:52:11          0:00 A0 0.00 h - cl0 0.00
 m 19:52:16          0:00 A0 0.00 h - cl0 0.00
 i 19:52:16          Job: 54df0504… []
 m 19:52:21          0:00 A0 0.00 h - cl0 0.00
cl 19:52:22 cl-0     4.10 GB of DAG data generated in 44,060 ms.
 m 19:52:26          0:00 A0 184.16 Kh - cl0 184.16
 m 19:52:31          0:00 A0 1.96 Mh - cl0 1.96
 m 19:52:36          0:01 A0 1.98 Mh - cl0 1.98
 i 19:52:39          Job: d3b1da5e… []
 m 19:52:41          0:01 A0 1.99 Mh - cl0 1.99
cl 19:52:43 cl-0     Job: 54df0504… Sol: 0x778d000001d14c71
 i 19:52:43          **Accepted 150 ms. []
 m 19:52:46          0:01 A1 1.95 Mh - cl0 1.95
 m 19:52:51          0:01 A1 2.07 Mh - cl0 2.07
 m 19:52:56          0:01 A1 2.00 Mh - cl0 2.00
 m 19:53:01          0:01 A1 1.98 Mh - cl0 1.98
 i 19:53:01          Job: ccc2b97f… []
 m 19:53:06          0:01 A1 1.97 Mh - cl0 1.97
 i 19:53:07          Job: 23919d82… []
^C i 19:53:10 main     Got interrupt ...
 i 19:53:10 main     Disconnected from []
 i 19:53:10 main     Shutting down miners...
 i 19:53:16 main     Terminated!

Code is available at

Is it worth it?

Um. Not really. At current Ethereum prices (2021-02-26), it generates $0.14 of profit per day. It’s still a profit, but very miniscule.

LAN Party VPN – My experience with WinTun, Windows Firewall, NSIS and Windows Defender

I’ve recently made LAN Party VPN. It makes playing LAN games with friends on Discord effortless. LPVPN is written in C++, mostly because Discord SDK is only available in C, C++ and C#. So I didn’t have much of an option here.

The idea of LPVPN is rather simple, get IP packets from WinTun (the Windows implementation of TUN by WireGuard), and send those using the Discord SDK, and of course the other way around. There were still a few things left on the table: IP assignment and package routing. Both of these were implemented in the most straight-forward way possible. A minimal GUI is slapped on to LPVPN to make everything a little bit more user friendly.

I did ran into a few problems though:

Problem 1: Windows Firewall

Windows by default sets a network as “Public” with no easy way to reconfigure. A network interface being set to the “Public” profile gives Windows PCs a little bit more safety when connected to public networks. But in this case it totally destroys the intended use-case: LAN gaming. When a network is set to “Public”, other parties cannot connect to services on that interface, even if it listens to This means that if you will not be able to find and connect to games other people are hosting. I’m able to work around this using this API, which is able to set a network as “Private”.

Problem 2: Packing everything into a single binary

I want users to download a standalone exe file, double click it, and have the VPN running. This proved to be a little bit hard since I need to bundle 2 dlls with the main binary: one for WinTun, and one for Discord. I want to package everything into a single binary since I don’t want users to download a ZIP file and manually unzip. For this I used NSIS, which is usually used for creating software installer. In this case I used it as a way to automatically unpack the dlls and exe and then execute in a temporary directory. This works on paper until I run into problem 3.

Problem 3: Windows Defender False Positives

Unfortunately, as soon as I have the NSIS executable packaged. Windows Defender promptly deleted it from my computer and gave me a stern warning that it is a Trojan. (It’s kind of funny to see Windows do that.) This would not fly if I plan on distributing the software.

Problem 4: Problem 2, Electric Boogaloo

Since there’s really no easy way to fix problem 3. I have think of another way to solve problem 2. Fortunately I found MemoryModule, which is a reimplementation of LoadLibrary and its friends in Windows. This means I can load dlls from memory, instead of from files. I ended up packing the dlls as resources using cmrc, and then loading them using MemoryModule. This fixed the false positives from Windows Defender.

Wasmino = WASM + Arduino (Running Arduino code in browser)

Wasmino Running the “Fade” example

Wasmino is an Arduino core fully compatible with the Arduino IDE. It allows you to compile your Arduino sketch to WebAssembly and run it in a browser, all using the Arduino IDE.


This is the off-the-shelf “Fade” example in the Arduino IDE.


  This example shows how to fade an LED on pin 9 using the analogWrite()

  The analogWrite() function uses PWM, so if you want to change the pin you're
  using, be sure to use another PWM capable pin. On most Arduino, the PWM pins
  are identified with a "~" sign, like ~3, ~5, ~6, ~9, ~10 and ~11.

  This example code is in the public domain.

int led = 9;           // the PWM pin the LED is attached to
int brightness = 0;    // how bright the LED is
int fadeAmount = 5;    // how many points to fade the LED by

// the setup routine runs once when you press reset:
void setup() {
  // declare pin 9 to be an output:
  pinMode(led, OUTPUT);

// the loop routine runs over and over again forever:
void loop() {
  // set the brightness of pin 9:
  analogWrite(led, brightness);

  // change the brightness for next time through the loop:
  brightness = brightness + fadeAmount;

  // reverse the direction of the fading at the ends of the fade:
  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
  // wait for 30 milliseconds to see the dimming effect

You can see it compiled to WASM and running here:

(Click the “Run” button and the LED should start to animate)

Setting it up

Disclaimer: The toolchain is only tested on Windows (x86-64) and Linux (x86-64), and probably will work for MacOS. It 100% won’t work on 32bit systems since some dependencies (WASI and Binaryen) are 64bit only.

You’ll need your Arduino IDE installed first.

Go to File -> Preferences, and add to “Additional Boards Manager URLs”.

Install “Wasmino Virtual Boards” in Tools -> Board -> Board Manager. Select Wasmino as as target board.

Write some Arduino code, click upload, and an URL will be generated for you. (If you are uploading for the first time, it will ask you to login to your GitHub account. It uses GitHub Gist for saving the compiled WASM binary. The login times out in 60 seconds.)

Paste that URL into your browser and use the UI to add LEDs or toggle switches. The URL will automatically update as you make changes. You can then copy the resulting URL in the browser and share it with other people.


The Wasmino should be viewed as having an infinitely fast CPU, and operations other than delay and delayMicroseconds do not take any CPU time. Calling time functions micros and millis should reflect this. IO can only happen during sleep, therefore you cannot do

while (!digitalRead(2)) {} // this will hang the browser tab

and should do this instead

while (!digitalRead(2)) {

Having a infinitely fast CPU also means that interrupts will only happen during delay and delayMicroseconds. It is still good practice to use noInterrupts and interrupts to protect critical sections if you plan to use your code on real hardware.

Other caveats exist such as the lack of BUILTIN_LED macro since Wasmino, being virtual, doesn’t have a built-in LED.

What works and what doesn’t work


y: working
n: not working

Digital I/O
y   digitalRead()
y   digitalWrite()
y   pinMode()
Analog I/O
y*  analogRead()
n   analogReference()
y   analogWrite()
Zero, Due & MKR Family
n   analogReadResolution()
n   analogWriteResolution()
Advanced I/O
n   noTone()
n   pulseIn()
n   pulseInLong()
n   shiftIn()
n   shiftOut()
n   tone()
y   delay()
y   delayMicroseconds()
y   micros()
y   millis()
y   abs()
y   constrain()
y   map()
y   max()
y   min()
y   pow()
y   sq()
y   sqrt()
y   cos()
y   sin()
y   tan()
n   isAlpha()
n   isAlphaNumeric()
n   isAscii()
n   isControl()
n   isDigit()
n   isGraph()
n   isHexadecimalDigit()
n   isLowerCase()
n   isPrintable()
n   isPunct()
n   isSpace()
n   isUpperCase()
n   isWhitespace()
Random Numbers
n   random()
n   randomSeed()
Bits and Bytes
y   bit()
y   bitClear()
y   bitRead()
y   bitSet()
y   bitWrite()
y   highByte()
y   lowByte()
External Interrupts
y   attachInterrupt()
y   detachInterrupt()
y   interrupts()
y   noInterrupts()
y*  Serial
y   Stream
n   Keyboard
n   Mouse

*   Not exposed in the demo web app.
*   Not exposed in the demo web app.

The Wasmino Arduino core is available at

The runtime is also available at

Canon EOS R5 Thermal Mod

Modding my own R5

Required Tools / Material

  • PH0 Screw Driver (it must be PH0, anything else won’t do)
  • Tweezers (Optional, but nice to have)
  • Thermal Pads (1mm thick, preferably uncut)
  • Thermal Pads (2mm thick, preferably uncut)
  • Scissors (to cut the thermal pads)


If you break your camera, it’s not my fault. I also don’t guarantee it will stop overheating in your environment as it depends greatly on the ambient temperature. Adding a fan will almost certainly help.


Remove these screws.

I recommend removing the screws in the order of bottom -> left -> right -> front. If the screws are not visible, they are either under the faux-leather cover (which you can pry up with your finger nails, you don’t need to fully remove the faux-leather to get to the screws), or under the eye-piece cover, which is held in with two screws.

I also recommend printing out the image above, and place the screws in the red circles as you go.

After you removed these screws, SLOWLY lift up the front panel by the card slot and the screen hinge. It may take a little force to dislodge, but it shouldn’t be very hard. There’s one flex cable to disconnect, make sure it’s fully disconnected before it fully comes apart.

R5 thermal mod: screws to remove from PCB. Image From

Remove the cable shown in the red box, and remove the screws in the red circles. You should now be able to flip the green board upwards.

R5 thermal mod: apply thermal pads

Remove the original white thermal pads covering the memory chips. After removing factory thermal pads, apply new thermal pads according to the image above:

  • Blue: 1.5mm – 2mm (stack thermal pads if not thick enough)
  • Green: 1.0mm

Reinstall the green board. Check from sides that the thermal pads are making good contact. (Don’t forget to reconnect the cable you disconnected!)

EOS R5 thermal mod: add more thermal pads. Image From

Cover the blue region with 2mm thick thermal pad (stack thermal pads if not thick enough)

EOS R5 thermal mod: scrape the black blob off. Image From

Scrape the black pad off the back cover shown in the image above.

Reassemble everything back. Don’t brute force it.


In my testing, with 15 degree Celsius ambient, I am able to record unlimited DCI 8K All-I. I have to format the card every 10 minutes or so but otherwise I’m able to record until the battery dies. Have fun!


With room temperature (25C) it will still overheat in 8K. However sticking a fan next to it fixes it. See for proof.


Another outdoor test (Outdoors, 23°C):

A few observations:

  1. After thermal mod, outdoors at 23°C, I’m able to get 48 minutes of 8K.
  2. The countdown timer works more like an internal temperature measuring.
  3. After overheating, with 8 minutes of cooldown, I’m able to get another 28 minutes of 8K.

CFExpress – M.2 Adapter, Now with better Signal / Power Integrity and Open Source

This is a newer iteration of the CFExpress – M.2 Adapter, with better routing to ensure signal integrity on the M.2 side. And the PCBs are now copper poured for both GND and +3.3V on the M.2 side.


Bill of Materials:

PCSMfgrPart NumberDescription
1TE Connectivity1-2199230-5CONN EDGE DUAL FMALE 67POS 0.020

More CFExpress (CFX) Card Experimentation – PCB Design for a Passive CFExpress – M.2 Adapter

As I mentioned in a previous post, it seems that it’s totally possible to convert a NVME SSD to a CFExpress Card. So here’s my first take on that.

Adapter PCB Layout

J1 is the PCB edge connector for the CFExpress card, J2, J3 are FFC (ribbon cable) connectors, and J4 is the M.2 connector.

I’m 99% sure that this will not work on the first try. But I think it has a good chance of working after a few iterations. I have sent my gerber files to OSHPark for fabrication, wish me luck.

Upgrading the Ventilation System on my K40

The K40 (or at least the version I have), has a pretty insufficient / badly designed ventilation system. It features a fan that just barely moves air, and a metal shroud that is supposed to direct the fumes toward the fan but unfortunately blocks the working area by about 1 inch (the working area is not very big in the first place mind you).

You can kind of see the metal shroud in this product listing photo

So there are two issues needing two solutions. (Duh.)

For the fume shroud, I attempted to cut it down with a circular saw. It almost killed my hearing and barely dented the shroud. (Kudos for using 1mm thick stainless steel, my K40 manufacturer!)

Time to design a new shroud and 3D print it!

Model is here.

Getting some M4 metal inserts in (I used this set of metal inserts on Amazon)


And we also need to augment the wimpy fan, so I bought a 4” inline fan from Amazon, and along with that some extra duct.

Inline fan installed. Notice the ghetto vent / window adapter made out of a cardboard box 🙂

Now it works better!