For the last year I've been using Fusion 360. Solidworks is too expensive for what I do, I haven't really liked Rhino or ViaCad, OpensCAD or FreeCAD. For the most part it works pretty good. There is however one problem I constantly encounter and that is the auto update mechanism leaves a lot to be desired. Chrome does this extremely well and I wish Autodesk would replicate their excellent system because at least once a month the Fusion 360 app either changes location in my dock or disappears completely. The disappearing completely is the biggest problem because then I have to go searching for it. Usually it lives here:
/users/<username>/library/application support/autodesk/webdeploy/production/Autodesk Fusion 360.app
So hopefully this saves someone some time.
Monday, February 10, 2020
Fusion 360 On Mac
Posted by
Chris Bensen
at
7:00 AM
1 comments
Monday, January 27, 2020
Raspberry Pi Overlay Root Filesystem
Let's Begin
initramfs image that contains the overlay module and a boot script to mount our root partition with the overlay. (All this could of course be compiled into the kernel image, but initramfs-tools(8) is an easier way to learn about the early init-process.)echo overlay >>/etc/initramfs-tools/modules
/etc/initramfs-tools/scripts/overlay (download):# Local filesystem mounting -*- shell-script -*-
#
# This script overrides local_mount_root() in /scripts/local
# and mounts root as a read-only filesystem with a temporary (rw)
# overlay filesystem.
#
. /scripts/local
local_mount_root()
{
local_top
local_device_setup "${ROOT}" "root file system"
ROOT="${DEV}"
# Get the root filesystem type if not set
if [ -z "${ROOTFSTYPE}" ]; then
FSTYPE=$(get_fstype "${ROOT}")
else
FSTYPE=${ROOTFSTYPE}
fi
local_premount
# CHANGES TO THE ORIGINAL FUNCTION BEGIN HERE
# N.B. this code still lacks error checking
modprobe ${FSTYPE}
checkfs ${ROOT} root "${FSTYPE}"
# Create directories for root and the overlay
mkdir /lower /upper
# Mount read-only root to /lower
if [ "${FSTYPE}" != "unknown" ]; then
mount -r -t ${FSTYPE} ${ROOTFLAGS} ${ROOT} /lower
else
mount -r ${ROOTFLAGS} ${ROOT} /lower
fi
modprobe overlay
# Mount a tmpfs for the overlay in /upper
mount -t tmpfs tmpfs /upper
mkdir /upper/data /upper/work
# Mount the final overlay-root in $rootmnt
mount -t overlay \
-olowerdir=/lower,upperdir=/upper/data,workdir=/upper/work \
overlay ${rootmnt}
}
initramfs image:update-initramfs -c -k $(uname -r)
/boot/initrd.img<kernel_version> - you can rename it to match your kernel file if you want./boot/config.txt - where initrd7.img is the initramfs image you created in the previous step:kernel=kernel7.img
initramfs initrd7.img
boot=overlay to the beginning of /boot/cmdline.txt.Kernel Panic
boot=overlay from cmdline.txt to boot as normal.less /usr/share/initramfs-tools/init
less /usr/share/initramfs-tools/scripts/local
Finishing Touches
Read-Only /boot
/boot filesystem is still mounted rw. You can protect it as well by adding ro to the boot partitions mount options in /etc/fstab:PARTUUID=72a9e9a9-01 /boot vfat defaults,ro 0 2
overctl
Usage: overctl [-h|-r|-s|-t|-w]
-h, --help This message
-r, --ro Set read-only root with overlay fs
-s, --status Show current state
-t, --toggle Toggle between -r and -w
-w, --rw Set read-write root
/usr/local/sbin and mark the file as executable. You will also need to create the following files containing the cmdline.txt options that you wish to toggle between:/boot/cmdline.txt.orig
/boot/cmdline.txt.overlay
motd(5)
#!/bin/sh
str=$(mount | grep ' on / ')
if echo $str | grep -q 'overlay'; then
printf "\n------ INFO: / MOUNTED WITH OVERLAY ------\n\n"
elif echo $str | grep -q 'rw'; then
printf "\n++++++ INFO: / MOUNTED READ-WRITE ++++++\n\n"
else
printf "\n!!!!!! WARNING: / UNKNOWN STATE !!!!!!\n\n"
fi
/etc/update-motd.d/80-overlay and make sure the file is executable.
Posted by
Chris Bensen
at
7:00 AM
0
comments
Labels: Raspberry Pi
Wednesday, January 8, 2020
Sony vs Canon: DSLR and Mirrorless
I had the Sony A9 and AIII and just couldn't use them because they are way too small for my hands. I think there has been so much attention on mirrorless, as well as marketing money to influencers on YouTubers which makes it seem like everyone is using Sony. I get it, mirrorless has it's place, and there are some great features. there's also some terrible features. I currently use a number of Canon DSLR cameras including the Canon EOS R. The R isn't my favorite camera, but it has grown on me. Anytime I reach for a camera, certainly for traveling, that is what I bring 90% of the time. The R and a Canon 7GX MII has been with me to 4 continents over the last year and I honestly wouldn't trade them for any Sony, despite having access to them. So here is my list of pluses and minuses:
- Blackout: The A9 has no blackout, all other mirrorless cameras have way too much blackout in my opinion. Canon R blackout is better than any Sony other than A9 but impossible to oh take photos of whales because they move too fast. Any DSLR such as Canon 5D MIV work fine for sports and wildlife photography, I just don't get the complaining of DSLR blackout. A DSLR instantly refreshes, where a mirrorless displays the previous image for way way too long and then refreshes. Only the A9 is usable for any wildlife or sports photography period.
- In body image stabilization (IBIS) I really don't understand why everyone wants IBIS. IBIS actually is a detractor if you want a seriously durable camera because the sensor shakes around.
- Smaller bodies are good for small hands, but not good for big hands. I have tried every battery grip available for the A series and they just don't work. The new body design is better, but not even close to a Canon or Nikon. Also the grip is shallow enough and the lens release is at the base of the body so when I grab the camera I've released the lens more than a few times.
- Sony can't handle water. If a Sony looks at water it stops working. Where I have dropped my 1D in river water and they keep working.
- Sony has no service support. When a camera needs servicing Sony suggest throwing it away and buying a new one. Canon has CPS, which is awesome if you need a camera serviced. Absolutely ridiculous Sony. No professional can depend on a Sony because of this.
- Shake a Sony camera and it makes noise. Strange.
- Love Sony A9 silent shooting, amazing for Wedding photography. Absolutely a game changer.
- I like the flip out LCDs on Canon better than Sony. Also the touchscreen. The menu system on Canon is way better too. The video codec is a little better on Sony.
- Canon color science is the best there is. Nikon is actually really great too. Hasselblad is pretty awesome too. Sony, not so much, every photo and video needs to be adjusted to look good. But Nikon video is lacking.
- Weight. Anyone that tells me that a Sony weighs less is crazy. Put a comparable lens on both cameras and compare the weight, they are basically the same and actually the Sony weighs more, just with a tiny camera in your hands. If you put a super small lens on a Sony then you have a weight savings, but meh. Now size is a bit smaller for traveling which is nice. But go for a micro 4/3 if size is a real issue.
- What is really nice about mirrorless is there's a shutter release button for photos and a record button for video. You don't have to switch between modes and remember do I look through the eyepiece or the rear screen.
- Lastly, all camera's do their best autofocus in good light. However, mirrorless cameras have poor autofocus anytime the light is anything less than good. The reason for this is what you see is what you get, but that is also what you autofocus from, where a DSLR will flip the mirror down and open up the lens aperture, evaluate then flip everything back to take the photo. This can take time, but 16 FPS for the pro series and 5FPS for the low end is pretty darn good.
Posted by
Chris Bensen
at
9:12 PM
0
comments
Friday, December 20, 2019
Get MAC Address for a Pi Cluster
At this point I'm going to digress a little. When building a large Raspberry Pi cluster at some point it's worth network booting. I'd say after 8 the cost of network booting is worth it. Just the time and expense of buying and cloning eight network cards. Network booting a Pi isn't terribly difficult, it's doing it consistently with a lot of them that's the problem. The netboot on a Pi times out after a short period of time, it isn't implemented 100% to spec, I could go on but these are things you can find out with some research. Although it's buried so if people want me to document these issues let me know in the comments. If there are any problems netbooting the Pi can be dead in a big cluster with no way to reboot it except reboot the entire thing. It helps to have a static IP address. Best way of doing this is by using DHCP (yeah, that's confusing) but map the MAC address to an IP address. Oh, also if you have enough Raspberry Pi, the MAC address may not be unique. We have two Pi with the same MAC addresses. Most clusters won't run into this. Also, network booting while not difficult requires the use of overlay file system. The problem comes when combining the two which is rather difficult. I got overlay file system working. Roy merged netboot and overlay and Vijay did this with Oracle Linux. There are a few other options netboot and redirect all writes to separate files and hope you got all them, create an NFS mount for each Pi (yeah, no). The only downside to netboot + overlay file system is you can't run Docker, which means you can't run K8 because Docker uses overlay and you can't use overlay on top of overlay.
OK, back to the reason for this blog post. My new system for obtaining the MAC address is as follows:
1. Install Raspbian on an SD card. I used belenaEtcher on Mac just because it's easy.
2. Copy the following Python script to the Pi and call it pimac.py
3. Create a cron job:
sudo crontab -e
4. Add this line to the end of the file:
@reboot python /home/pi/pimac.py &
5. Copy the following Python script to your desktop and run it. I'm running it in Visual Studio Code.
Note: If you decide you want to remove the dictionary in the code above to allow duplicate MAC addresses to print out, which his a great debugging tool and since UDP is not guaranteed is a perfectly fine way to go, then you might want to remove duplicates. To do this in Visual Studio Code this is one way I found to do it:
- Control+F
- Toggle "Replace mode"
- Toggle "Use Regular Expression" (the icon with the
.*symbol) - In the search field, type
((^[^\S$]*?(?=\S)(?:.*)+$)[\S\s]*?)^\2$(?:\n)? - In the "replace with" field, type
$1
Posted by
Chris Bensen
at
7:00 AM
0
comments
Labels: Raspberry Pi
Tuesday, December 17, 2019
Shopify Payment System
It's a long story, but I have been dealing with an issue with Shopify for well over a year now. Many websites use them as their Payment system behind their merchant account. And they are very convenient, if one knew what they were getting into. Unfortunately there is absolutely zero disclosure on any of the websites that use it how this will work, how to edit your stored information or how to delete the data they stored. This is a direct breach of the GDPR, Informatique et Libertés as well as the new California data privacy law CCPA.
So with about a year of working with all of the merchants and Shopify support, I finally can report there is a way to opt out of Shopify:
https://pay.shopify.com/optout
Posted by
Chris Bensen
at
7:00 AM
0
comments
Monday, December 9, 2019
Sparkfun MP3 Shield
I have used a lot of these MP3 player shields in projects. They are super awesome and super simple to use, but I figured a small writeup was worthwhile as there are some details that aren't in the official documentation as well as some tips I have when using the shield.
The MP3 shield uses just about every pin on the Arduino. The documentation states you can use D5, D10 and A0-A5. However, I have found that for some reason when using the shield I have found neither D5 nor D10 to be usable. So typically what I do is setup a software serial using two of the analog pins:
#include <SPI.h>
#include <SdFat.h>
#include <SFEMP3Shield.h>
#include <SoftwareSerial.h>
SdFat sd;
SFEMP3Shield MP3player;
SoftwareSerial input(A0, A1); // RX, TX
void setup() {
Serial.begin(115200);
input.begin(9600);
input.flush();
if(!sd.begin(9, SPI_HALF_SPEED)) {
sd.initErrorHalt();
}
if (!sd.chdir("/")) {
sd.errorHalt("sd.chdir");
}
uint8_t result = MP3player.begin();
if(result != 0) // check result, see readme for error codes.
{
Serial.println("error");
Serial.print(F("Error code: "));
Serial.print(result);
}
// Loudest volume for left and right
MP3player.setVolume(0, 0);
Serial.println("Ready");
}
void loop() {
if (input.available()) {
Serial.println("reading");
String action = readFromSerial();
Serial.println(action);
input.flush();
if (action == "play") {
Serial.println("Playing");
uint8_t result = MP3player.playMP3("awesome.mp3");
delay(3000);
Serial.println("Ready again");
}
}
}
String readFromSerial() {
static char buffer[80];
if (readline(input.read(), buffer, 80) > 0)
{
Serial.println(buffer);
}
return buffer;
}
int readline(int readch, char *buffer, int len)
{
static int pos = 0;
int rpos;
if (readch > 0) {
switch (readch) {
case '\n': // Ignore new-lines
break;
case '\r': // Return on CR
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len-1) {
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
// No end of line has been found, so return -1.
return -1;
}
Posted by
Chris Bensen
at
3:21 PM
1 comments
Monday, November 18, 2019
Using Max Build Volume of Ultimaker 3D Printer
Sometimes you have to print at the max build volume of your 3D printer. You'll get the error: "does not fit build volume". I have an Ultimaker and the easiest slicer to use is Cura. And sometimes I want the full build volume of 330 x 240 x 300 mm. If you set the "skirt link count" to zero, it won't print the line around the part so you can get the full x and/or y. As you may know the reason for the skirt is so that the printer gets a consistent extrusion of the filament, but I you need the full bed size, then you need the full bed size. Also turn off brim and set build plate adhesion to "none". Lastly set "travel avoid distance" to 0. Do it in that order for best results.
Posted by
Chris Bensen
at
7:00 AM
0
comments
Tuesday, November 5, 2019
Raspberry Pi Super Computer STL Files Published on Thingiverse
Finally. I'm sorry it took so long. Here it is, the files to print your very own 2U rack mountable Raspberry Pi holder for 21 Raspberry Pi 3B+. Let me know if you have any questions or problems. Just because we made over 50 of these, doesn't mean it's easy.
https://www.thingiverse.com/thing:3958586
Posted by
Chris Bensen
at
7:00 AM
1 comments
Labels: Raspberry Pi
Monday, September 2, 2019
Tuesday, August 20, 2019
Arduino Eithernet Shield 2 NTP Time
I'm trying to get an accurate time on an Arduino. I have an Ethernet Shield 2 attached to an Arduino Mega. There's also a couple current sensors and a barcode scanner. The main reason for this post is for those people that happen to have the Ethernet Shield 2 on any Arduino, and want to get the time. Every single example on the internet or in the Arduino IDE examples doesn't work, except for this one:
https://www.arduino.cc/en/Tutorial/UdpNtpClient
So as of the time of reading this, that tutorial will most likely be out of data, but let's say for the next 6 months it'll be the place to go.
So, here's a little insight into what I'm doing and my decisions along with some code at the end:
1. I have an Arduino. That means no clock.
2. I have an Ethernet Shield 2. That means there are an abundance of clocks on the internet.
3. I am not hooking up a clock to the Arduino. That means I need to use the Arduino to keep time.
4. So I'm going to do this by polling a clock on the internet, keeping that time around and adding the time since boot to that time. It isn't a clock you'd want to use as your morning alarm clock, but it'll be accurate enough for what I need.
So I hacked together two functions from that tutorial:
unsigned long getCurrentTime() {
unsigned long result = 0;
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
if (Udp.parsePacket()) {
// We've received a packet, read the data from it
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
// the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, extract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = ");
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
Serial.print("Unix time = ");
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
Serial.println(epoch);
result = epoch;
}
else {
result = 2099414000; // Picked a date so we can test when not connected to the internet.
}
return result;
}
String timeToString(unsigned long value) {
String result = "";
unsigned long epoch = value;
// print the hour, minute and second:
result += String((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
result += String('-');
if (((epoch % 3600) / 60) < 10) {
// In the first 10 minutes of each hour, we'll want a leading '0'
result += String('0');
}
result += String((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
result += String('-');
if ((epoch % 60) < 10) {
// In the first 10 seconds of each minute, we'll want a leading '0'
result += String('0');
}
result += String(epoch % 60); // print the second
return result;
}
unsigned long startTime;
void setup() {
// ...
startTime = getCurrentTime();
}
And anytime you want the current time viola!
timeToString(startTime + millis() / 1000)
Posted by
Chris Bensen
at
7:00 AM
2
comments
Wednesday, July 24, 2019
Fusion 360 - Threads Not Exported to STL
There is one thing that I always have problems with, so hopefully by writing this short blog post I'll remember more often. That problem is creating threads properly. Usually I noticed after I have already printed the part. Always remember to choose the "Modeled" check box. I wish I could make this the default.
Also, if you need to remove part of the hole after the threads in the timeline, you'll have thread remnants, so move the threads to be later in the timeline, then "Cut" the hole.

Posted by
Chris Bensen
at
7:00 AM
0
comments
Thursday, July 18, 2019
Very Large Raspberry Pi Cluster - Part VI
At this point I've shown how the all the Pi will be held in place to make a 2U rack, and next is the overall structure but that will have to wait until next week. So for this post I wanted to talk a little about the software and what we're going to run on all these Pi. A lot of data will be processed and sent to the cluster. So here's the high level of the software stack:
- Each Pi will network boot from a central server running Oracle Linux
- The boot image will be running Oracle Linux
- It will be running Kubernetes and Docker
- There will be a very large video wall run by a Windows server
- Everything will be written in Java
- Gluon is helping
Posted by
Chris Bensen
at
7:00 AM
0
comments
Labels: Raspberry Pi




