Category Archives: Raspberry Pi

Running Puppeteer in a Docker container on Raspberry Pi

Puppeteer is a Node.js module that allows interacting with a (headless) web browser programmatically. This is extremely useful for automating website testing, generating screenshots and PDFs of web pages or programmatic form submission.

Docker offers numerous benefits including a standardized environment, isolation, and rapid deployment to name a few. These benefits might be why you’d want to run Puppeteer inside a Docker container. Unfortunately, doing so on Raspberry Pi is not straightforward. There are a few issues that make it harder than usual. Luckily, they are all solvable. Let’s take a look.

Problem 1: Chromium included in Puppeteer does not work on Raspberry Pi

Puppeteer by default downloads a matching version of Chromium which is guaranteed to work out of the box on supported platforms. Unfortunately, Chromium does not currently provide an arm build that works on Raspberry Pi and running stock Puppeteer on Raspberry Pi will end up with a crash. This can be solved by installing Chromium with apt-get install chromium -y and telling Puppeteer to use it by passing  the executablePath: '/usr/bin/chromium' to the launch() function as follows:

   const browser = await puppeteer.launch({
        executablePath: '/usr/bin/chromium',
        args: []
    });

When doing this, Puppeteer no longer should need to download Chromium as it will be using the version installed with apt-get, so it makes sense to skipping this step by setting the corresponding environment variable in the Dockerfile:

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

This will significantly reduce the time needed to install node modules.

Problem 2: The base image installs an old version of Chromium

Some node Docker images are based on old distributions that contain only older versions of Chromium. Most notably the node:16 image is based on buster. If you use this base image the only version of Chromium you will be able to install with apt-get is 90.0.4430.212-1. Unfortunately this version doesn’t work in a Docker container – it just hangs indefinitely. Moving to the node:16-bullseye base image allows installing a much newer version of Chromium (108.0.5359.124) where this is no longer a problem.

Problem 3: Puppeteer crashes on launch

Puppeteer will not launch in a Docker container without additional configuration. Chromium is not able to provide sandboxing when running inside a container so it needs to be launched at least with the --no-sandbox argument. Otherwise it will crash with the following error message

Failed to move to new namespace: PID namespaces supported, Network namespace 
 supported, but failed: errno = Operation not permitted

Sandbox is a security feature and running without a sandbox is generally discouraged. Unfortunately, running without a sandbox appears to be currently the only way to run Puppeteer inside a Docker container. In the past the --no-sandbox option required running Puppeteer as root, only increasing the risk. Luckily, this no longer seems to be the case – it is possible now to launch puppeteer with the --no-sandbox option as a non-privileged user.

There are a few more options that might be worth exploring if launching Puppeteer inside a container fails:

  • --disable-gpu – disables GPU hardware acceleration (which is usually not available when running in Docker)
  • --disable-dev-shm-usage – prevents from using shared RAM (/dev/shm/)
  • --disable-setuid-sandbox – disabled setui sandbox

Putting everything together

The information provided above should be all that is needed to be build a Docker image for a Node.js app that uses Puppeteer and runs on Raspberry Pi. Below is an example Dockerfile for such a Docker image. It contains comments to make it easy to notice how the solutions discussed above were applied.

# Ensure an up-to-date version of Chromium 
# can be installed (solves Problem 2)
FROM node:16-bullseye 
# Install a working version of Chromium (solves Problem 1)
RUN apt-get update
RUN apt-get install chromium -y
ENV HOME=/home/app-user
RUN useradd -m -d $HOME -s /bin/bash app-user 
RUN mkdir -p $HOME/app 
WORKDIR $HOME/app
COPY package*.json ./
COPY index.js ./
RUN chown -R app-user:app-user $HOME
# Run the container as a non-privileged user (discussed in Problem 3)
USER app-user
# Make `npm install` faster by skipping 
# downloading default Chromium (discussed in Problem 1)
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
RUN npm install
CMD [ "node", "index.js" ]

Because the application also requires a couple modifications to how the headless browser is launched here is a small example application illustrating these changes with comments:

const puppeteer = require('puppeteer');
(async() => {
    const browser = await puppeteer.launch({
        // use Chromium installed with `apt` (solves Problem 1)
        executablePath: '/usr/bin/chromium',
        args: [
            // run without sandbox (solves Problem 3)
            '--no-sandbox',
            // other launch flags (discussed in Problem 3)
            // '--disable-gpu,
            // '--disable-dev-shm-usage',
            // '--disable-setuid-sandbox',
        ]
    });
    const page = await browser.newPage();
    await page.goto('https://www.google.com/', {waitUntil: 'networkidle2'});
    let e = await page.$('div#hplogo');
    let p = await e?.getProperty('title');
    if (p) {
      console.log(`Today's doodle: ${await p.jsonValue()}`);
    } else {
      console.log('No Doodle today :(');
    }
    browser.close();
})();

Finally, here is the output of this application when run in a container:

Both the application and the Dockerfile are also available on Github

Conclusion

Running Puppeteer inside a Docker container is tricky – especially, when doing so on Raspberry Pi. The post discussed the key obstacles and provided solutions to overcome them. In addition, a demo containerized app was included to illustrate the main points.

Advertisement

Using Raspberry Pi to flash ESP8266

Necessity is mother of invention. Even though in this case there was not a lot of invention, there was necessity. I wanted to play with the ESP8266 module and I wanted it badly. Unfortunately, I could not find any FTDI in my drawers, so I had to find a different way of talking to my ESP8266. I quickly concluded that I could use either Arduino or Raspberry Pi. I decided to start with Arduino but for whatever reason could not make it work. A little desperate, I decided to try Raspberry PI. This, thanks to the information I found on the Internet, got me much further. I found a few articles scattered over the web and after combining various pieces of information I was able not only to talk to my ESP8266 but also reprogram it. I decided to write this post so that all the information I found is in one place. Let’s get started.

The idea is to connect an ESP8266 module to Raspberry Pi’s GPIO serial pins and then use a serial communication terminal like minicom or screen to talk to the module. Before we can do this, we need to reconfigure our Raspberry Pi so that the OS is not using the serial interface. The first step is to disable serial logins. The easiest way to achieve this is to use raspi-config. Start the the raspi-config with sudo raspi-config, go to Interfacing Options and then to Serial. You will be asked two questions – answer them as follows:

  • Would you like a login shell to be accessible over serial?
    Select: No
  • Would you like the serial port hardware to be enabled?
    Select: Yes

The second thing to do is to switch off sending bootup info to serial by removing references to ttyAMA0 in the /boot/cmdline.txt file – e.g.

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=PARTUUID=9815a293-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

becomes:

dwc_otg.lpm_enable=0 console=tty1 root=PARTUUID=9815a293-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

Finally, you need to restart your Raspberry Pi for changes to take effect

sudo reboot now

Now, that we prepared our Raspberry Pi we need to wire the ESP8266 module. You need to power down Raspberry PI and connect your ESP8266 module as per the diagram below.

RPI_ESP8266_normal_mode


ESP8266 Raspberry Pi
GND       GND
3.3V      3.3V
RXD       UART_TXD
TXD       UART_RXD
CH_PD     3.3V (via a pull down resistor)

After wiring the ESP8266 module to the Raspberry Pi has been completed we can boot the Pi and connect to the ESP8266 using screen (I am using screen because it is installed by default but you can use minicom if you prefer):

screen /dev/ttyAMA0 115200

To see if we connected successfully let’s send some commands to the module (note: when using screen you need to press Enter and CTRL+J to send the command).

Check if AT system works (AT):

AT
OK

Display the version (AT+GMR)

AT+GMR
AT version:1.2.0.0(Jul  1 2016 20:04:45)
SDK version:1.5.4.1(39cb9a32)
Ai-Thinker Technology Co. Ltd.
Dec  2 2016 14:21:16
OK

Connect to WiFi (a series of commands)

AT+CWMODE_CUR=1
OK

AT+CWJAP_CUR="ssid","pwd"
WIFI CONNECTED
WIFI GOT IP
OK

AT+CWJAP_CUR?
+CWJAP_CUR:"ssid","18:a6:f7:23:9e:50",6,-52
OK

AT+CIFSR
+CIFSR:STAIP,"192.168.0.110"
+CIFSR:STAMAC,"5c:cf:7f:36:cd:31"

The full list of AT commands can be found on the espressif’s web site in the documents section – look for ESP8266 AT Instruction Set. Note that some commands from the document may not work if you are using an older version of the firmware.

We are able to communicate with our ESP8266. Now, let’s try updating the firmware. First, we need to power down the PI again and connect the GPIO0 pin of the ESP8266 to the PI’s ground pin. This will make the module run in the flash mode.

RPI_ESP8266_flash_mode

The next step is to install the esptool which we will use to transfer firmware files to the module:

sudo apt-get update
sudo apt-get install python
sudo apt-get install python-pip
sudo pip install esptool

Finally, we need a firmware that we will flash to the device. We can get a new firmware by cloning the espressif’s ESP8266_NONOS_SDK  repo:

git clone https://github.com/espressif/ESP8266_NONOS_SDK

After the repo has been cloned we need to go to the bin folder and run the following command:


esptool.py --port /dev/ttyAMA0 --baud 115200  write_flash --flash_freq 40m --flash_mode qio 0x0000 boot_v1.7.bin 0x1000 at/512+512/user1.1024.new.2.bin 0x7E000 blank.bin

The output of the command should look like this:

esptool.py v2.0.1
Connecting...
Detecting chip type... ESP8266
Chip is ESP8266
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 1MB
Flash params set to 0x0020
Compressed 4080 bytes to 2936...
Wrote 4080 bytes (2936 compressed) at 0x00000000 in 0.3 seconds (effective 121.5 kbit/s)...
Hash of data verified.
Compressed 427060 bytes to 305755...
Wrote 427060 bytes (305755 compressed) at 0x00001000 in 27.9 seconds (effective 122.3 kbit/s)...
Hash of data verified.
Compressed 4096 bytes to 26...
Wrote 4096 bytes (26 compressed) at 0x0007e000 in 0.0 seconds (effective 4538.5 kbit/s)...
Hash of data verified.

Once the process is complete we can power down the Raspberry Pi and disconnect the ESP8266 GPIO0 pin from GND to make it run again in the normal mode. Now, we can start the PI, and dump the ESP8266 firmware version with the AT+GMR command:


AT+GMR
AT version:1.4.0.0(May  5 2017 16:10:59)
SDK version:2.1.0(116b762)
compile time:May  5 2017 16:37:48
OK

This is a much newer version than what we originally had so we were able to flash our ESP8266 successfully. Yay!

Acknowledgements:

The following articles were instrumental in writing this post:

 

 

Home Automation with Raspberry Pi – Garage Door

I hate keys. And even though I hate not having keys when I need them even more I still think keys are annoying. I keep forgetting or losing them. In some cases, they make life harder than it needs to be. For example, I bike to work almost every day from early spring to late fall and I should not even have to think about keys and door since I just need to open the garage door, take/park my bike and close the garage door. Currently what I need to do is to make a couple trips around the house to open/unlock doors and then close/lock everything I opened/unlocked. I tried using my kids to do all of this for me but they simply started to avoid seeing me in the morning and even if I caught them they kept forgetting to do what I asked. Yes, I could carry a remote with me. I actually did for a while but it was even worse than carrying the key – it’s bigger, it’s another thing to remember about and the last one got some rust within a year or so and stopped working completely. There is, however, one thing I carry with me almost always – my cell phone. If I could open my garage from my cell phone (almost) all my problems would be solved. As a bonus my kids wouldn’t be afraid of leaving their rooms before I leave for work. Yes, I know there are already some garage openers on the market that can be controlled with cell phones so one way to go would be just to replace my garage opener with one like that. I don’t like, however, throwing away things that work. Replacing the garage opener would also cost at least a few hundred bucks even if I did all the work myself. Another thing is that I am currently using a Windows Phone (hey, an unlocked Nokia Lumia 640 was like $30 + tax – this deal was hard to beat for a cheapo like me) and given Windows Phone’s market share I don’t expect any garage opener to have an app for that. Finally, hacking something myself is much more fun then using a ready made solution.

Enough talking, let’s get started. What you need for this project is a Raspberry Pi running Raspbian and a remote that matches your garage opener. If you don’t have a spare remote you can buy a generic one on eBay or Amazon. (I had a remote with a broken button so it was useless in general but perfect for this project since we need to short-circuit the button anyways). The idea is to connect the remote to the Raspberry Pi via GPIO (General Purpose Input/Output) and then write a web application that could be used to control the remote. Whenever you are on your home network you will be able to access this web application (e.g. from your phone) and open or close the garage door. Heck, if you have friends who you trust and allow on your network they will be able to open your garage door to (this obviously has pros and cons). This might sound complicated but it is really not that hard.

GPIO (General Purpose Input/Output)

The first thing you need to be familiar with is GPIO (General Purpose Input/Output). If you have never heard about GPIO these are the 26 pins on your Raspberry Pi. You can find out more on GPIO from this tutorial.

Hardware

Preparing the hardware requires a bit of soldering. Don’t be afraid – it is so basic that even I could do that! The first thing to take care of is the button you press to open or close the garage door. Because we are going to control the remote from Raspberry Pi we need to short-circuit the button otherwise it will prevent the current from flowing and nothing will work. We also need to solder jumper wires which we will connect to GPIO. Note – GPIO pins are 3.3V and my garage opener used a 3.3V battery so I soldered the wires directly to the battery socket. This is what I ended up with :

Connecting remote to Raspberry PI

The important thing before connecting anything is to know what version of Raspberry PI you have because the layout of GPIO pins vary between models and revisions. In this project I used pin 7 (GPIO4) because it seems to be the same in most/all of the models and revisions. I would recommend however double checking your Raspberry Pi before wiring things up (if you decide to use a different pin you will have to update the script that controls GPIO).  Now, you just need to connect the ‘hot’ wire to pin 7 (GPIO 4) and the other wire to GND (e.g. pin 6). This is how things look like after I put them together:

Connected

Hint – for testing instead of connecting the remote you can connect just a LED. This way you can quickly test if things work without leaving your neighbors wondering why the hell your garage door opens 10 times in a minute. The GPIO tutorial shows how to do this and here is what I used for testing:

LED

Software

With hardware ready we can now take care of software. We want to be able to control GPIO from a web server. The problem is that you need root permissions to access GPIO and granting root permissions to a webserver is generally a bad idea. Fortunately, there is the pigpio library which allows to control GPIO. pigpio contains a utility – pigpiod – that launches pigpio as a daemon and enables talking to pigpio using sockets or the pipe. You talk to the pigpio library by sending pigpio messages. The list of available messages with descriptions can be found here. One very useful thing is that when pipgpiod is running you can send messages directly from command line by writing to /dev/pigpio – e.g. echo "w 4 1" > /dev/pigpio sets GPIO4 to high (w – write, 4 – GPIO4, 1 – high) – we will use this command in out script.

Thanks to pigpiod the webserver no longer needs to have root permissions to control GPIO.

I chose lighttp as my webserver. Installing and configuring it is not hard and there are quite a few tutorials on the internet showing how to set it up. Once you have a webserver up and running you only need a script that will handle user requests and send messages to pigpiod. I wrote the script in Python so you will need to install Python on your Raspberry Pi if you have not done it yet.

If you have never configured a daemon (like me – I just used the init script template I found here and adapted it) or written anything in Python (like me – but it’s like 7 lines of code) fear not. All the code is ready. Just clone my repo (or even copy files) from https://github.com/moozzyk/GarageDoor and follow steps below:

  1. Install pigpio
  2. Configure pigpiod (pigpio daemon)
    • copy the pigpiod script from the GarageDoor/scripts/init.d folder to /etc/init.d/. You must be root to do this. If you are in the GarageDoor repo root run:
      sudo cp scripts/init.d/pigpiod /etc/init.d/
    • Make sure the pigpiodscript you copied has execute permissions. If it does not you need to chmod +x it. Again, you have to have root permissions to do that:
      sudo chmod +x /etc/init.d/pigpiod
    • Start pigpiod daemon with:
      service pigpiod start
      (You can stop the service with service pigpiod stop or check the status with service pigpiod status)
    • If you have a test setup with LED you can now easily check if things work – set GPIO4 to high by running
      echo "w 4 1" > /dev/pigpio
      The LED should light up. To turn off the LED execute
      echo "w 4 0" > /dev/pigpio
    • Configure the daemon to start on boot:
      sudo update-rc.d pigpiod defaults
  3. Configure lighttp to execute python scripts (if you are not running lighttp configure your webserver accordingly). In /etc/lighttpd.config:
    • In the server.modules section uncomment or add "mod_cgi" e.g.:
      server.modules = (
              "mod_simple_vhost",
              "mod_evhost",
              "mod_userdir",
              "mod_cgi",
              "mod_compress"
      )
    • In the cgi.assignsection add a mapping for .pyfiles:
      cgi.assign = (
              ".py" => "/usr/bin/python"
      )
  4. Prepare and run the application
    • copy the garage.py file from GarageDoor/Script/web to /var/www (or wherever your application root is)
    • Navigate to the application using a browser – you should see something like this:
      wp_ss_20160522_0002
    • Now if everything is set up correctly your garage door should open/close if you click the open/close button

Points of interests

Currently the application lets everyone who is on the home network play with your garage door. This may not be desirable. One improvement to make would be to authorize people before they can open or close the door. Another useful thing would be to log when the garage door is opened and closed. Finally, configuring the webserver to use https would be useful as well.

Raspmodore 360

My brother is visiting us from overseas and I thought it would be cool to set up some kind of a retro gaming environment we could use to refresh our memories from childhood when we spent hours playing M.U.L.E., Kikstart 2 or HatTrick on our Commodore 64. Raspberry Pi seemed a perfect choice – it’s close to the TV (or already connected if you are using Raspbmc) and it has access to the network which makes it easy to acquire software. To play games however you need some kind of a controller. So, what else is close to the TV? An XBox! And it actually has some controllers (let’s forget about Kinect for now). And this is how I came up with the idea of something I called:

Raspmodore 360

Raspmodore 360

– a Commodore 64 emulator running on Raspberry Pi and using XBox 360 controllers. Setting things up turned out to be relatively easy. First you need to install Vice emulator. You can find some steps here but they don’t actually work. Firstly they refer to the “squeeze” distro and you most likely have the “wheezy” distro (the “regular” Raspbian image recommended on the Raspberry Pi download page). Secondly the Raspbian “wheezy” image is a hard float image and when running Vice installed from ftp.uk.debian.org it just dies with the Segmentation Fault error. After my last battle with Raspberry Pi, Mono and EF6 I think it might be possible to install the package from ftp.uk.debian.org on the Soft-float Debian “wheezy” distro but this distro is a bit slower so I kept looking for a version I could run directly on Raspbian. Soon I found this thread which points to something that seems like a private version of Vice built for armhf architecture. If you just follow the steps (btw. notice the _armhf suffix in the Vice package – the package from ftp.uk.debian.org does not have this suffix and I believe this is the reason it doesn’t work on Raspbian):

wget http://www.frank-buss.de/raspberrypi/vice_2.3.dfsg-4_armhf.deb
dpkg -i vice_2.3.dfsg-4_armhf.deb
wget http://www.zimmers.net/anonftp/pub/cbm/crossplatform/emulators/VICE/old/vice-1.5-roms.tar.gz
tar -xvzf vice-1.5-roms.tar.gz
cp -a vice-1.5-roms/data/* /usr/lib/vice/

you will end up with a working C64 emulator on your Raspberry Pi (don’t forget to copy ROMs or you will have an opportunity to see yet another Segmentation Fault error – verified empirically). Just run a console window in the X Window environment and start Vice emulator with the x64 command.
[Spoiler Warning] Had I run any game at this point I would probably have given up setting up the controllers but I did not and instead[/Spoiler Warning] I moved on to setting up the controllers. It seemed to be very easy. There is this xboxdrv thingy you install and stuff should just work (note: you will need the XBox 360 controller “for Windows” – it contains a standalone USB receiver which you will need to plug into your Raspberry Pi (your “Windows” today)). To install xboxdrv you just need to run the following commands:

sudo apt-get update
sudo apt-get install xboxdrv

Just to see if/that the controller works I started the xboxdrv and turned on verbose output

sudo xboxdrv --v

synced the controller and started pressing buttons and pushing sticks. Unfortunately I did not see any activity on the screen. I “reset” the controller (i.e. removed and replaced batteries) pressed the button on the receiver to initiate syncing and turned on the controller. Again it synced fine but I could not see any activity in the xboxdrv window regardless of what I did to the controller. I even restarted my Raspberry Pi and repeated the process but it still did not help. I found a small app called jstest-gtk that allows testing joysticks but it could not find any controllers attached. I learnt about a few interesting debugging options built in xboxdrv (like debugging at the USB protocol level) but this did not help much. With that I started thinking that it must have been a problem with the controller so I grabbed a different one turned it on and… it synced immediately. That was unexpected since I did not press the sync button on the receiver. This is when I realized that the Problem Existed Between Chair And Keyboard (PEBCAK). I had been trying to sync the controller by pressing the button on the receiver but had not pressed the sync button on the controller itself. Therefore the controller started the Xbox located in a different room and then was syncing with it rather than with the receiver connected to the Raspberry Pi. A complete fail on my side. It took me a while to get over my lameness but when I did the things went smoothly. I disconnected my Xbox and really synced my controller with the receiver connected to my Raspberry Pi. xboxdrv woke up and jstest-gtk finally showed that there is a controller present. I configured Vice to use arrow keys as joystick and space as the fire button. The only remaining thing was to map the controller joystick and button(s). This is not difficult and can be done just by providing parameters when starting xboxdrv – just like this:

sudo xboxdrv –ui-axismap x1=KEY_LEFT:KEY_RIGHT,y1=KEY_UP,KEY_DOWN –ui-buttonmap a=XK_space

I started the emulator and ran a game. The controller worked fine – I was able to use it but I was not really able to play any game. Vice was way too slow – games ran just at 5-7 frames per second and the terminal window I used to start Vice from constantly showed the message:

Warning – Your machine is to slow for current settings

I played for 10 more minutes and gave up. That’s too bad. I still don’t quite understand why Raspberry Pi is not able to handle this. I ended up digging up the original Xbox from my garage. It has an ancient version of XBMC but with a working Commodore 64 emulator. We used it instead of Raspberry Pi. Unfortunately when playing M.U.L.E. pirates helped my brother by stealing all my smithore twice (the second time in the 11th round) and I lost the game. It probably would not have felt that bitter had it happened on Raspberry Pi.

Raspberry Pi + Mono + EF6 – defeat!

After (or maybe even before) I successfully made EF6 work on Mono I thought it would be cool to try it on Raspberry Pi. Since my project already worked on Mono I thought it should be relatively easy to do. The first thing I had to do was to install Mono on my Raspberry Pi. It is easy and requires running just two commands:

sudo apt-get update
sudo apt-get install mono-runtime

Since my project uses MySQL I also had to install MySQL database. Again it was as easy as executing:

sudo apt-get install mysql-server

After that I used mysql and mysqladmin tools to create a user and a database that would be used by my app. With my Raspberry Pi ready I copied my little app to the Raspberry Pi and ran it using the mono command:

mono EF6MySqlTest.exe

only to see it failing with the following exception:

Missing method .ctor in assembly /home/pi/monotest/MONOTest/EntityFramework.dll, type
System.Runtime.CompilerServices.ExtensionAttribute
Can't find custom attr constructor image: 
 /home/pi/monotest/MONOTest/EntityFramework.dll mtoken: 0x0a00006a

Unhandled Exception: System.TypeLoadException: Could not load
type 'System.Runtime.CompilerServices.ExtensionAttribute' from assembly 'EntityFramework'.
  at EF6MySqlTest.SimpleContext..ctor () [0x00000] in :0
  at EF6MySqlTest.Program.Main (System.String[] args) [0x00000] in :0
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Could not load
type 'System.Runtime.CompilerServices.ExtensionAttribute' from assembly 'EntityFramework'.
  at EF6MySqlTest.SimpleContext..ctor () [0x00000] in :0
  at EF6MySqlTest.Program.Main (System.String[] args) [0x00000] in :0

The exception startled me a little bit. The very first thing I noticed was that the app failed even before it started executing. I looked at the documentation for the ExtensionAttribute type on the MSDN but I did not find anything extraordinary. Only comparing the documentation for .NET Framework 4.5 with the documentation for .NET Framework 4 gave me the clue. In the .NET Framework 4.5 the ExtensionAttribute type lives in the mscorlib.dll while in the .NET Framework 4 it lived in the System.Core.dll assembly. In general it is OK. CLR has this feature where a type can be moved from one assembly to another as long as its full name does not change and the original assembly contains the TypeForwardedToAttribute assembly level attribute pointing to the assembly the type lives in the new version (this is called type forwarding and you can find more details here). The exception I got just said that the EntityFramework assembly depends on the ExtensionAttribute which could not be found. Putting the information together I inferred what the exception did not say – namely that the ExtensionAttribute type could not be found in the mscorlib.dll assembly. This was just one step away from solving my problem. If the mscorlib.dll did not contain the ExtensionAttribute type then the type had to be in the System.Core.dll assembly which in turn meant that the version of the .NET Framework supported by Mono packages I installed was 4 and my application was compiled targeting version 4.5. This was not a big deal. Entity Framework 6 supports both .NET Framework 4.5 and .NET Framework 4 so I went back and recompiled my app so that it targets .NET Framework 4, copied it to my Raspberry Pi started again and… failed again. This time the exception looked like this:

Unhandled Exception: 
System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.Config.DbConfigurationManager 
---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.SqlTypes.SqlDecimal 
---> System.OverflowException: Can't convert to SqlDecimal, Out of range
  at System.Data.SqlTypes.SqlDecimal..ctor (Byte bPrecision, Byte bScale, Boolean fPositive, Int32 data1, Int32 data2, Int32 data3, Int32 data4) [0x00000] in :0
  at System.Data.SqlTypes.SqlDecimal..cctor () [0x00000] in :0
  --- End of inner exception stack trace ---
  at System.Data.DataColumn..ctor (System.String columnName, System.Type dataType, System.String expr, MappingType type) [0x00000] in :0
  at System.Data.DataColumn..ctor (System.String columnName, System.Type dataType) [0x00000] in :0
  at System.Data.Common.DbProviderFactoriesConfigurationHandler.CreateDataSet () [0x00000] in :0

  [... omitted for brevity ...]

  --- End of inner exception stack trace ---
  at System.Data.Entity.DbContext.InitializeLazyInternalContext (IInternalConnection internalConnection, System.Data.Entity.Infrastructure.DbCompiledModel model) [0x00000] in :0
  at System.Data.Entity.DbContext..ctor (System.String nameOrConnectionString) [0x00000] in :0
  at EF6MySqlTest.SimpleContext..ctor () [0x00000] in :0
  at EF6MySqlTest.Program.Main (System.String[] args) [0x00000] in :0

The good thing was that the previous exception was gone and that I could see some familiar Entity Framework methods in the stack trace. This means that this time the application at least tried running. But then I got to the System.OverflowException: Can't convert to SqlDecimal, Out of range exception thrown from System.Data.SqlTypes.SqlDecimal..ctor and started feeling a bit uneasy. This did not look like a bug in my code. On the contrary, it looked like some problem in Mono which I most likely would not be able to fix or work around. To confirm that it was unrelated to my app or EF6 I played a bit and came up with the following program that duplicated the issue (yes, it is just one line of code and it probably could be simplified even more):

public static void Main()
{
  Console.WriteLine(DbProviderFactories.GetFactoryClasses().Rows.Count);
}

Running the above code resulted in the same exception I saw when running my app:

Unhandled Exception: System.TypeInitializationException: 
An exception was thrown by the type initializer for System.Data.SqlTypes.SqlDecimal —>
System.OverflowException: Can’t convert to SqlDecimal, Out of range
 at System.Data.SqlTypes.SqlDecimal..ctor (Byte bPrecision, Byte bScale, Boolean fPositive, Int32 data1, Int32 data2, Int32 data3, Int32 data4) [0x00000] in :0
 at System.Data.SqlTypes.SqlDecimal..cctor () [0x00000] in :0

I looked around a little bit and found two bugs (bug report 1 and bug report 2) that looked similar to what I was seeing. One of them even contained a link to a patch with the fix which made me think that this might be fixed in a newer version of Mono. Soon I found this page which mentions a Debian Experimental version which is 3.0.6 as opposed to 2.10.8 installed on the Raspberry Pi. I tried to follow the steps listed on the page but unfortunately I could not install the experimental version due to missing package dependencies:

Reading package lists... Done
Building dependency tree
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
mono-complete : Depends: mono-devel (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-mcs (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-gmcs (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-dmcs (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-csharp-shell (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-2.0-gac (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-2.0-service (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-4.0-service (= 2.10.8.1-5) but it is not going to be installed
Depends: monodoc-base (= 2.10.8.1-5) but it is not going to be installed
Depends: monodoc-manual (= 2.10.8.1-5) but it is not going to be installed
Depends: libmono-cil-dev (= 2.10.8.1-5) but it is not going to be installed
E: Unable to correct problems, you have held broken packages.

I am not a Linux guru but really wanted to make it work (or at least understand why it did not want to work) so I asked a question on stackoverflow and wrote an email to Mirco Bauer who seems to overlook Mono 3.0 on Debian. Unfortunately I have not received any answers which would help me resolve the problem so I gave up and forgot about this when suddenly after a couple weeks I got a response from Mirco. He wrote:

Mono has no working armhf port, but there is now work done to create an official armhf port. The only option for Mono is armel right now.

I have to admit that when I read the message I did not really understand it. So I did some research and found this post. It enlightened me and made me realize that the bug report I looked at before and the Raspberry Pi downloads page already had the answer I needed. I should have just tried understanding some details included there instead of ignoring them :). To put it simply
– the Raspbian “wheezy” is an armhf distro (and – as per email from Mirco – “Mono has no working armhf port”) while the Soft-float Debian “wheezy” is an armel image. The difference between the images is how they handle floating point operations. This gave me some hope. I downloaded the Soft-float Debian “wheezy” image and quickly prep my Raspberry Pi by installing mono and MySQL. One of the very first thing I did was to run the repro app to verify if the OverflowException is gone. Indeed this time instead of the exception the app wrote 7 (I wish it had been 42 but 7 is still better than OverflowException). This started looking promising so I tried to run the “proper” app. I got a few uninteresting exceptions (like no permissions to access registry etc.) but then I got this:

Unhandled Exception: System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet ---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V1ConventionSet ---> System.TypeLoadException: Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention' from assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
  --- End of inner exception stack trace ---
  at System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Data.Entity.DbModelBuilder.SelectConventionSet (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration modelConfiguration, DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModelBuilder () [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModel (System.Data.Entity.Internal.LazyInternalContext internalContext) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.RetryLazy`2[System.Data.Entity.Internal.LazyInternalContext,System.Data.Entity.Infrastructure.DbCompiledModel].GetValue (System.Data.Entity.Internal.LazyInternalContext input) [0x00000] in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet ---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V1ConventionSet ---> System.TypeLoadException: Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention' from assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
  --- End of inner exception stack trace ---
  at System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Data.Entity.DbModelBuilder.SelectConventionSet (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration modelConfiguration, DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModelBuilder () [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModel (System.Data.Entity.Internal.LazyInternalContext internalContext) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.RetryLazy`2[System.Data.Entity.Internal.LazyInternalContext,System.Data.Entity.Infrastructure.DbCompiledModel].GetValue (System.Data.Entity.Internal.LazyInternalContext input) [0x00000] in <filename unknown>:0

The most important thing here is Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention' from assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.. I looked at the KeyAttributeConvention and the class is very simple. There is actually hardly anything that could break here. Since the only dependency this class has is the KeyAttribute type it had to be the thing that caused problems. Again, I tried to isolate the issue so I added the following line to my test app:

Console.WriteLine(typeof(System.ComponentModel.DataAnnotations.KeyAttribute).Name);

After I did this the test app failed to compile complaining that it cannot resolve the KeyAttribute type and recommended adding references to an assembly containing a definition of this type even though I did provide a reference to System.ComponentModel.DataAnnotations.dll. This was weird. I decided to list all the types from the System.ComponentModel.DataAnnotations.dll assembly to make sure that the KeyAttribute is there so I replaced the line referring to the KeyAttribute with:

foreach(var t in typeof(ScaffoldColumnAttribute).Assembly.GetTypes())
{
    Console.WriteLine(t.FullName);
}

After running the app I looked at the type names and could not find the KeyAttribute type. This is unfortunate since it means that it’s impossible to run EF6 on the stable version (2.10.8) of Mono I installed. I thought the attribute might have been added in a newer version so I tried installing the experimental version. Unfortunately this did not really work. This time I did not get any errors but I got the message mono-complete is already the newest version and nothing was installed.
This was an interesting journey and I am a bit disappointed I was not able to achieve what I wanted especially because the goal seemed so close. The bright side is that I learnt quite a lot of interesting things and I believe it will be possible to run EF6 on the Raspberry Pi when either Mono works on armhf or there is a newer version of Mono for armel available. I will give it a try for sure in the future!