RPi Soldering Scope

I’m thinking of making a soldering microscope from a Raspberry Pi (either a spare B+ I have or maybe a ZeroW). I had looked at Andonstar ADMSM201 or Lapsun 14MP HDMI 180x scopes, but they each have their limitations – crap software, tiny focal length requiring additional lenses, £200+ pricetags…..

So looking at it, I just have to buy a Pi camera module with a CS mount and a decent lens. Then I can either stream the display or plug into a HDMI monitor. Even without a Barlow lens I should get at least 20cm working area beneath the camera.

The only problem is how to mount it. I’m thinking of a threaded rod from a 3D printer attached to my wooden 3rd arm board and some sort of 3D printed bracket. I only really need up/down not left/right or forwards/backwards (I can just move whatever I’m soldering). I even thought of a laboratory retort stand.

I’m going to try without illumination initially, and potentially make something from a Neopixel ring that could be controlled by the Pi and potentially powered from the same USB supply.

BOM is currently:

I’ve made a pi camera to CS mount adaptor and I’ve also modified my Pi ZeroW case to include a screw-on CS mount adaptor and holes for a shutdown/reset button and wires for a Neopixel ring which I’ll probably use instead of a regular LED ring, or I may use a 12v PSU and some spare LED strip.

Big Update

Its been quite a while since my last post, so here’s a big one…..

I’ve been doing a lot of 3D printing since I received my FlashForge Creator Pro 2016. I learned OpenSCAD and FreeCAD, my designs are here. I’ve made various enclosures for Arduino’s and Raspberry Pi’s, as well as USBASP cases, voltmeter cases, solder tip holders, calliper battery savers, resistor forming tools, soldering helping hands, bottle openers, trolley tokens, SD card holders, PIR cases, Dremel accessories, various 3D printer upgrades and the usual calibration prints.

Yesterday I upgraded Dad’s Lenovo B570 laptop to Ubuntu 16.04 from 12.04, which was fun due to its broken EFI setup – basically it has a BIOS and not SecureBoot but the installer sees it as using GPT+EFI instead of a simple MBR. Weirdness such as failure to boot from USB and then failure to boot from HDD once I’d installed from DVD ensued. Boot-Repair fixed the problem, from what I can see it installed some dummy EFI files in the EFI partition, which the 12.04 install obviously nuked. Had to disable hardware acceleration in Chrome to prevent Flash flickering, but otherwise it seems to be working fine.

Today I modified an SG90 servo for continuous rotation, basically removed the wiper on the potentiometer and replaced it with a couple of SMD resistors, and cut the tab off of the largest gear. I found that there is no trimpot to adjust the zero position on my servo so I altered my code to send 100 instead of 90 for “stop”, which I found using some trial and error. Values lower than 100 move clockwise (the lower the number, the faster movement) and greater than 100 moves anti-clockwise (the higher, the faster).

When you write a value to the servo, you’re no longer giving it a degree value, you’re setting a pusle width. So instead of “rotate to 40 degrees” you’re saying “run for 1200uS”, just like a DC motor with H-bridge, which is essentially what is inside a servo. The upside is that the servo can rotate 360 degrees, the downside is that you don’t know where its rotated to as the microcontroller receives no feedback.

My test code is below – its moves it left, right, fast, slow and stop.

continuous_servo.ino

#include <Servo.h>

// create servo object
Servo myservo;

// speeds for my sg90
#define FASTCW  45
#define SLOWCW  90
#define STOP    100
#define FASTACW 135
#define SLOWACW 110

void setup()
{
    // attach servo to D9
    myservo.attach(9);
}

void loop()
{
    myservo.write(FASTCW);
    delay(2000);

    myservo.write(STOP);
    delay(1000);

    myservo.write(FASTACW);
    delay(2000);

    myservo.write(STOP);
    delay(1000);

    myservo.write(SLOWCW);
    delay(2000);

    myservo.write(STOP);
    delay(1000);

    myservo.write(SLOWACW);
    delay(2000);
    
    myservo.write(STOP);
    delay(1000);
}

Makefile

BOARD_TAG = uno
MONITOR_PORT = /dev/ttyUSB0
include /usr/share/arduino/Arduino.mk

I’ve also been making custom power cables lately, such as a USB to DC jack for giving 5v to older projects such as a 555 astable from a powerbank. It seems that microUSB should be the new go-to connector for power instead of 2.1×5.5mm DC jacks these days. I’ve actually got some USB-to-DIP breakout boards as well as the TP4056 Li-On charger boards. I’ve also got some USB boost regulators on the way that apparently can output up to 28v or something silly. My next custom cable will likely be banana plugs to pin headers as its a pain trying to put a male pin header on a screw terminal.

Finally I had a play with a MCP23017 port expander which I was thinking of using when my ESP8266 projects don’t have enough pins – like my RTC clock could have done with 1-2 more buttons. But it seems like a lot of wiring e.g. 8 pins before you’ve even added any I/O, and a lot of calls to the Wire library e.g. 4 calls just to send an output like lighting an LED, so I think I’ll hold out for ESP32’s to be generally available before doing any more pin-intensive projects.

Motion Sensing Nightvision Camera

I fancied figuring out if it was my cat or the neighbourhood cats pooping in my side alley (oh er missus!) so thought I’d find a use for my PiNoIR camera and that spare B+

I usually use RaspiMJPEG for webcam sorts of things like timelapse or somesuch, but it seems in a state of flux – the internal motion-detection system simply doesn’t work it would seem, and the external system is still using Motion and not the MMAL version, so its slow and can only cope with about VGA resolution on a B+, and it doesn’t seem to work in timelapse mode, and seems flaky at best anyway.

MotionEyeOS seemed like it was much more professional and motion-detection with timelapse worked really well, but again was limited to pretty low resolutions with weird aspect ratios. Also doesn’t seem to use motion-mmal anymore (think it did when it was motionpie).

Anyway, I figured the problem was software motion-detection using image analysis, so I decided to go the hardware route and use a PIR. So far I’ve come up with this Python script which is based on the excellent picamera module. It can do useful things like vertically flip the image (on the GPU!) which is handy as my Sainsmart camera module is a bit hard to mount the right way around!

Also the code uses interrupts so events don’t get missed and the CPU is mostly asleep. I chose to use 1080p resolution, but you could go up to to full 5MP (or 8MP on the new camera boards!) if you wanted to, forget 640×480

The only problem is that the PIR sensor doesn’t work through double-glazing, so I need to mount it in a box outside, which I was going to do eventually anyway.

#!/usr/bin/python

# import module
import picamera
import time
import RPi.GPIO as GPIO

# setup gpio mode
GPIO.setmode(GPIO.BCM)
PIR_PIN = 14
GPIO.setup(PIR_PIN,GPIO.IN)

# instantiate class
camera = picamera.PiCamera()

# vertical flip
camera.vflip = True

# set resolution
camera.resolution = '1080p'

# interrupt function
def onMotion(PIR_PIN):
    filename = time.strftime("image-%Y%m%d-%H%M%S.jpg")
    camera.capture(filename)

try:
    GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=onMotion)

    # loop until interrupted
    while 1:
        time.sleep(100)

except KeyboardInterrupt:
    GPIO.cleanup()

You have to run the script using sudo, or from an init script like /etc/rc.local perhaps, and maybe set a directory to write the files to rather than just cwd.

Edit: just bought a v1.3 Raspberry Pi Zero and cable for this, as it should use a lot less power than the B+ and is a lot smaller to encase.

Pi Zero Print Server

After looking at wireless print servers costing £35+ I decided to make my own using my spare Raspberry Pi Zero (original without camera connector).

I needed a wifi dongle and USB connection for the printer, so have bought a Zero4U USB hub HAT and case, in fact I bought two of each as the shipping was fixed, so it came to £23 shipped from the Czech Rebuplic, so shouldn’t take too long to arrive.

I’ve got a spare 16Gb Class4 microSD card – we’ll have to see how that goes, otherwise I have a 32Gb Transcend on order. I’ve got a 5v 2A power supply and DC jack to microUSB adapter, as I don’t fancy the official power supply (its upside-down and looks fragile).

I’ve got Ralink RT5370 and Realtek RTL8188 wifi dongles on order, although I already have a RT5370 to use, but its nice to have spares that cost a Pound or two!

I installed the latest Raspbian Lite and added cups and sane to the installation.

The printer is a Brother DCP-7055 and I know it works with Ubuntu 12.04/14.04 as it used to be connected locally, however that was using x86-64, I’m not sure ARM is supported by Brother’s drivers, it seems printing at least will work with the HL-1250 ppd file or possibly the 7045N Postscript driver or brlaser, but almost definitely the brscan4 driver won’t work as its x86-only.

I don’t think I can connect a laptop to the miniUSB port on the Zero4U whilst its connected to the Zero, so to scan I’ll have to unplug the Zero from the printer unless Brother can be convinced by my constant tweets/emails to make ARM packages!

In other news my acrylic weld glue and needle bottles have arrived, so got to have a go at assembling the ESP8266 clock cases soon – and Dremel that button hole a bit more.

Update: Brother confirmed no current or future plans to support the ARM platform. The DCp-7500 worked out of the box with Raspbian’s built-in brlaser3 installation and DCP-7030 PPD file. No chance in getting the scanner working though, scanimage didn’t even detect it.

The Zero4U is pretty cool, although the acrylic case is a bit weak, took 5-6 days to arrive. The miniUSB port is blocked by the case so you can’t use it at the same time with a Zero and desktop PC anyway.

I assembled the acrylic case for the ESP8266 clock, it was a bit of a nightmare with the weld solvent, but it went together really well. I even managed to add some rails to make the lid removable.

Alarm Clock Design

I’m thinking of building an alarm clock next, I need something that has bright lights that can be turned on and off, as my wall clock I can’t read at night. I’m probably going to use four MAX7219 controlled 8×8 LED matrices just to display the four digits. I was thinking of a 7-segment display, but that’s a bit small.

Also I’d like it to be NTP synced, so needs wifi. I’ll possibly use a Raspberry Pi Zero with wifi dongle, if I can get my hands on one, otherwise it’ll be an ESP-12; probably powered by a USB mains adaptor as the LED’s need 5v even though the zero/esp8266 are 3.3v

Not sure if its going to be just a clock or an alarm, I don’t think a piezo buzzer will be very good, so maybe a speaker.

I’ve already got a bunch of DS1307 RTC chips and 32.768KHz 12.5pF crystals, just have to power at 5v and use pullup resistors on SDA/SCL to 3.3v

I made a test sketch on the 4tronix ESP-12e breakout, which just prints the NTP time to the serial monitor, its based on the demo from the ESP8266WiFi library:

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

// local port to listen for udp packets
unsigned int localPort = 2390;

// ntp server pool
IPAddress timeServerIP;
const char* ntpServerName = "europe.pool.ntp.org";

// ntp time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;

// buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE];

// create a udp instance
WiFiUDP udp;

// send an ntp request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
    Serial.println("Sending NTP packet...");

    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);

    packetBuffer[0] = 0b11100011;   // li, version, mode
    packetBuffer[1] = 0;            // stratum, or type of clock
    packetBuffer[2] = 6;            // polling interval
    packetBuffer[3] = 0xEC;         // peer clock precision

    // 8 bytes of zero for root delay & root dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    // all ntp fields have been given values, send request
    udp.beginPacket(address, 123);
    udp.write(packetBuffer, NTP_PACKET_SIZE);
    udp.endPacket();
}

void setup()
{
    // debug
    Serial.begin(9600);

    // connect to wifi network
    WiFi.begin("SSID", "passphrase");

    // static ip, gateway, netmask
    WiFi.config(IPAddress(192, 168, 1, 2), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));

    while (WiFi.status() != WL_CONNECTED)
    {
        delay(200);
    }

    Serial.println("Starting UDP");
    udp.begin(localPort);
    Serial.print("Local port: ");
    Serial.println(udp.localPort());
}

void loop()
{
    // get a random server from the pool
    WiFi.hostByName(ntpServerName, timeServerIP);

    // send an ntp packet to a time server
    sendNTPpacket(timeServerIP);

    // wait to see if a reply is available
    delay(1000);

    int cb = udp.parsePacket();
    if (!cb)
    {
        Serial.println("No packet yet");
    }
    else
    {
        // we've received a packet, read the data from it
        Serial.print("Packet received, length=");
        Serial.println(cb);

        // read the packet into the buffer
        udp.read(packetBuffer, NTP_PACKET_SIZE);

        // the timestamp starts at byte 40 of the received packet and is four bytes,
        // or two words, long. first, esxtract 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);

        // utc is the time at gmt
        Serial.print("The UTC time is ");

        // print the hour - 86400 equals secs per day
        Serial.print((epoch % 86400L) / 3600);
        Serial.print(':');

        // in the first 10 minutes of each hour, we'll want a leading '0'
        if (((epoch % 3600) / 60) < 10)
        {
            Serial.print('0');
        }

        // print the minute (3600 equals secs per minute)
        Serial.print((epoch % 3600) / 60);
        Serial.print(':');

        // in the first 10 seconds of each minute, we'll want a leading '0'
        if ((epoch % 60) < 10)
        {
            Serial.print('0');
        }

        // print the second
        Serial.println(epoch % 60);
        Serial.println("");
    }

    // wait ten seconds before asking for the time again
    delay(10000);
}