The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Electronics Workbench

Electrical & Electronic gadgets

  • RAMPS 1.4: LCD Board QC FAIL

    When I plugged the LCD into the RAMPS board, the USB current jumped from 70-ish mA to about 700 mA, which seemed odd. Eventually the problem followed the “Smart Adapter Board”, which has no active components and simply rearranges two pin headers into two ribbon cables, so what could go wrong?

    This stared me in the face for a while until I recognized it:

    RAMPS 1.4 - Smart Adapter - solder mask failure
    RAMPS 1.4 – Smart Adapter – solder mask failure

    Yup, that trace is supposed to run around the corner without merging into the ground plane and, of course, it carries the +5 V power supply to the LCD board. Just another production goof and, I’m certain, the boards don’t get any testing because they’re so simple.

    Two cuts, a bit of scraping, a snippet of Wire-Wrap wire, and it’s all good:

    RAMPS 1.4 - LCD panel
    RAMPS 1.4 – LCD panel

    The white-on-blue display is reasonably legible in person, even if it’s nearly invisible here. Might have something to do with polarization vs. the Pixel’s camera.

    Everything else on the LCD board works fine. I set the beep to 50 ms and the tone to 700 Hz, which suit my deflicted ears better than the defaults.

    Phew!

  • RAMPS 1.4: Configuration for Generic Motor Control

    Configuring the knockoff RAMPS 1.4 board went reasonably smoothly:

    RAMPS 1.4 - First Light
    RAMPS 1.4 – First Light

    The DC (n.b., not an AC) solid state relay in the foreground switches the 20 V laptop supply brick to the RAMPS shield atop the knockoff Arduino Mega 2560, controlled by the PS_ON pin (black wire), with +5 V from a pin in the AUX header (yellow wire). The SSR includes a ballast resistor limiting the current to 12 mA, with an inconspicuous red LED behind the black dot showing when the output is turned on.

    Because it’s a DC SSR, polarity matters: the supply goes to the + terminal, the RAMPS power inputs to the – terminal.

    I haven’t applied much of a load to to the SSR, but it works as expected. Define POWER_SUPPLY 1 and PS_DEFAULT_OFF so the boards starts up with the SSR turned off, then use M80 / M81 to turn it on / off as needed.

    Remove D1 on the RAMPS board to isolate the Mega power from the +20 V supply. Stuffed as shown, the Mega draws 70 mA from the USB port, although an external 8 V (-ish) supply is always a good idea.

    The stepper is a random NEMA 17 from the heap in a mount intended for a DIY plotter. I adjusted the tiny trimpots on all the boards for 400 mA peak = 250 mA RMS into the windings, after finding 250 mApk didn’t produce nearly enough mojo, even for a demonstration:

    X Axis Stepper Drive
    X Axis Stepper Drive

    Just to get it running, I used DEFAULT_AXIS_STEPS_PER_UNIT = 100 step/mm, MAX_FEEDRATE 100 mm/s, and (for lack of anything better)
    DEFAULT_*_ACCELERATION 1000. Those all depend the torque produced by the motor current, which is still way too low.

    The endstops require X_???_ENDSTOP_INVERTING true.

    I set the ?_BED_SIZE parameters to a generous 2000, with ?_MIN_POS equal to -SIZE/2 to put the origin in the middle where I prefer it, with a similar setting for the Z axis. Obviously, those numbers don’t correspond to any physical reality.

    Three little 100 kΩ thermistors sprout from their header and produce reasonable temperatures, although (being cheap eBay parts) they may not match the Type 4 curve. I don’t have any heaters connected. All the over / under temperature lockouts are disabled, because I don’t care right now.

    The G-Code parser wants uppercase command letters, which means I get to press the Caps Lock key for the first time in nearly forever!

    The header along the right edge of the board connects to the LCD control board, which is another story.

    The diffs for the Configuration.h and Configuration_adv.h files as a GitHub Gist:

    77c77
    < #define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes.
    > #define STRING_CONFIG_H_AUTHOR "(Ed Nisley – KE4ZNU)" // Who made the changes.
    113c113
    < #define BAUDRATE 250000
    > #define BAUDRATE 115200
    126c126
    < //#define CUSTOM_MACHINE_NAME "3D Printer"
    > #define CUSTOM_MACHINE_NAME "Not a 3D Printer"
    130c130
    < //#define MACHINE_UUID "00000000-0000-0000-0000-000000000000"
    > #define MACHINE_UUID "89647f7b-2575-4809-bc90-5396f4376e02"
    225c225
    < #define POWER_SUPPLY 0
    > #define POWER_SUPPLY 1
    230c230
    < //#define PS_DEFAULT_OFF
    > #define PS_DEFAULT_OFF
    285c285
    < #define TEMP_SENSOR_0 1
    > #define TEMP_SENSOR_0 4
    290c290
    < #define TEMP_SENSOR_BED 0
    > #define TEMP_SENSOR_BED 4
    304c304
    < #define TEMP_WINDOW 1 // (degC) Window around target to start the residency timer x degC early.
    > #define TEMP_WINDOW 2 // (degC) Window around target to start the residency timer x degC early.
    309c309
    < #define TEMP_BED_WINDOW 1 // (degC) Window around target to start the residency timer x degC early.
    > #define TEMP_BED_WINDOW 2 // (degC) Window around target to start the residency timer x degC early.
    324,325c324,325
    < #define HEATER_0_MAXTEMP 275
    < #define HEATER_1_MAXTEMP 275
    > #define HEATER_0_MAXTEMP 75
    > #define HEATER_1_MAXTEMP 75
    329c329
    < #define BED_MAXTEMP 150
    > #define BED_MAXTEMP 75
    417,418c417,418
    < #define PREVENT_COLD_EXTRUSION
    < #define EXTRUDE_MINTEMP 170
    > //#define PREVENT_COLD_EXTRUSION
    > #define EXTRUDE_MINTEMP 50
    422c422
    < #define PREVENT_LENGTHY_EXTRUDE
    > //#define PREVENT_LENGTHY_EXTRUDE
    441,442c441,442
    < #define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders
    < #define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed
    > //#define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders
    > //#define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed
    476c476
    < #define ENDSTOPPULLUPS // Comment this out (using // at the start of the line) to disable the endstop pullup resistors
    > //#define ENDSTOPPULLUPS // Comment this out (using // at the start of the line) to disable the endstop pullup resistors
    490,495c490,495
    < #define X_MIN_ENDSTOP_INVERTING false // set to true to invert the logic of the endstop.
    < #define Y_MIN_ENDSTOP_INVERTING false // set to true to invert the logic of the endstop.
    < #define Z_MIN_ENDSTOP_INVERTING false // set to true to invert the logic of the endstop.
    < #define X_MAX_ENDSTOP_INVERTING false // set to true to invert the logic of the endstop.
    < #define Y_MAX_ENDSTOP_INVERTING false // set to true to invert the logic of the endstop.
    < #define Z_MAX_ENDSTOP_INVERTING false // set to true to invert the logic of the endstop.
    > #define X_MIN_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
    > #define Y_MIN_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
    > #define Z_MIN_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
    > #define X_MAX_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
    > #define Y_MAX_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
    > #define Z_MAX_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
    527c527
    < #define DEFAULT_AXIS_STEPS_PER_UNIT { 80, 80, 4000, 500 }
    > #define DEFAULT_AXIS_STEPS_PER_UNIT { 100, 100, 100, 100 }
    534c534
    < #define DEFAULT_MAX_FEEDRATE { 300, 300, 5, 25 }
    > #define DEFAULT_MAX_FEEDRATE { 100, 100, 100, 100 }
    542c542
    < #define DEFAULT_MAX_ACCELERATION { 3000, 3000, 100, 10000 }
    > #define DEFAULT_MAX_ACCELERATION { 1000, 1000, 1000, 1000 }
    552,554c552,554
    < #define DEFAULT_ACCELERATION 3000 // X, Y, Z and E acceleration for printing moves
    < #define DEFAULT_RETRACT_ACCELERATION 3000 // E acceleration for retracts
    < #define DEFAULT_TRAVEL_ACCELERATION 3000 // X, Y, Z acceleration for travel (non printing) moves
    > #define DEFAULT_ACCELERATION 1000 // X, Y, Z and E acceleration for printing moves
    > #define DEFAULT_RETRACT_ACCELERATION 1000 // E acceleration for retracts
    > #define DEFAULT_TRAVEL_ACCELERATION 1000 // X, Y, Z acceleration for travel (non printing) moves
    566,567c566,567
    < #define DEFAULT_ZJERK 0.4
    < #define DEFAULT_EJERK 5.0
    > #define DEFAULT_ZJERK 20.0
    > #define DEFAULT_EJERK 20.0
    744c744
    < #define INVERT_X_DIR false
    > #define INVERT_X_DIR true
    746c746
    < #define INVERT_Z_DIR false
    > #define INVERT_Z_DIR true
    774,775c774,775
    < #define X_BED_SIZE 200
    < #define Y_BED_SIZE 200
    > #define X_BED_SIZE 2000
    > #define Y_BED_SIZE 2000
    778,783c778,783
    < #define X_MIN_POS 0
    < #define Y_MIN_POS 0
    < #define Z_MIN_POS 0
    < #define X_MAX_POS X_BED_SIZE
    < #define Y_MAX_POS Y_BED_SIZE
    < #define Z_MAX_POS 200
    > #define X_MIN_POS -X_BED_SIZE/2
    > #define Y_MIN_POS -Y_BED_SIZE/2
    > #define Z_MIN_POS -1000
    > #define X_MAX_POS X_BED_SIZE/2
    > #define Y_MAX_POS Y_BED_SIZE/2
    > #define Z_MAX_POS 1000
    1013c1013
    < //#define EEPROM_SETTINGS // Enable for M500 and M501 commands
    > #define EEPROM_SETTINGS // Enable for M500 and M501 commands
    1023c1023
    < #define HOST_KEEPALIVE_FEATURE // Disable this if your host doesn't like keepalive messages
    > //#define HOST_KEEPALIVE_FEATURE // Disable this if your host doesn't like keepalive messages
    1046,1047c1046,1047
    < #define PREHEAT_1_TEMP_BED 70
    < #define PREHEAT_1_FAN_SPEED 0 // Value from 0 to 255
    > #define PREHEAT_1_TEMP_BED 60
    > #define PREHEAT_1_FAN_SPEED 255 // Value from 0 to 255
    1050,1051c1050,1051
    < #define PREHEAT_2_TEMP_BED 110
    < #define PREHEAT_2_FAN_SPEED 0 // Value from 0 to 255
    > #define PREHEAT_2_TEMP_BED 90
    > #define PREHEAT_2_FAN_SPEED 255 // Value from 0 to 255
    1275c1275
    < //#define REVERSE_ENCODER_DIRECTION
    > #define REVERSE_ENCODER_DIRECTION
    1290c1290
    < //#define INDIVIDUAL_AXIS_HOMING_MENU
    > #define INDIVIDUAL_AXIS_HOMING_MENU
    1307,1308c1307,1308
    < //#define LCD_FEEDBACK_FREQUENCY_DURATION_MS 100
    < //#define LCD_FEEDBACK_FREQUENCY_HZ 1000
    > #define LCD_FEEDBACK_FREQUENCY_DURATION_MS 50
    > #define LCD_FEEDBACK_FREQUENCY_HZ 700
    1379c1379
    < //#define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
    > #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
    65,66c65,66
    < #define THERMAL_PROTECTION_PERIOD 40 // Seconds
    < #define THERMAL_PROTECTION_HYSTERESIS 4 // Degrees Celsius
    > #define THERMAL_PROTECTION_PERIOD 100 // Seconds
    > #define THERMAL_PROTECTION_HYSTERESIS 5 // Degrees Celsius
    77c77
    < #define WATCH_TEMP_PERIOD 20 // Seconds
    > #define WATCH_TEMP_PERIOD 60 // Seconds
    85c85
    < #define THERMAL_PROTECTION_BED_PERIOD 20 // Seconds
    > #define THERMAL_PROTECTION_BED_PERIOD 120 // Seconds
    97c97
    < #define WATCH_BED_TEMP_PERIOD 60 // Seconds
    > #define WATCH_BED_TEMP_PERIOD 120 // Seconds
    225c225
    < *
    > *
    352c352
    < #define HOMING_BUMP_DIVISOR {2, 2, 4} // Re-Bump Speed Divisor (Divides the Homing Feedrate)
    > #define HOMING_BUMP_DIVISOR {4, 4, 4} // Re-Bump Speed Divisor (Divides the Homing Feedrate)
    374c374
    < #define DEFAULT_STEPPER_DEACTIVE_TIME 120
    > #define DEFAULT_STEPPER_DEACTIVE_TIME 0
    458c458
    < //#define LCD_INFO_MENU
    > #define LCD_INFO_MENU
    461c461
    < //#define STATUS_MESSAGE_SCROLLING
    > #define STATUS_MESSAGE_SCROLLING
    464c464
    < //#define LCD_DECIMAL_SMALL_XY
    > #define LCD_DECIMAL_SMALL_XY
    467c467
    < //#define LCD_TIMEOUT_TO_STATUS 15000
    > #define LCD_TIMEOUT_TO_STATUS 10000
    562c562
    < #define XYZ_HOLLOW_FRAME
    > //#define XYZ_HOLLOW_FRAME
    565c565
    < #define MENU_HOLLOW_FRAME
    > //#define MENU_HOLLOW_FRAME
    573c573
    < //#define USE_SMALL_INFOFONT
    > #define USE_SMALL_INFOFONT
    752c752
    < #define TX_BUFFER_SIZE 0
    > #define TX_BUFFER_SIZE 128
  • Streaming Radio Player: OLED SPI Speed Fix

    The OLED displays on the streaming radio players have SH1106 controllers supported by the Luma library, which works just fine. Digging into the source shows the default SH1106 setup (see the class spi() at the bottom) uses an 8 MHz clock:

        def __init__(self, spi=None, gpio=None, port=0, device=0,
                     bus_speed_hz=8000000, transfer_size=4096,
                     gpio_DC=24, gpio_RST=25):
    assert(bus_speed_hz in [mhz * 1000000 for mhz in [0.5, 1, 2, 4, 8, 16, 32]])
    

    Alas, the SH1106 doc suggests a maximum SPI clock of 2 to 4 MHz, the latter only with fair skies, a tailwind, and a stiff power supply:

    SH1106 OLED Controller - SPI timing
    SH1106 OLED Controller – SPI timing

    The display doesn’t get updated all that often, so there’s no point in rushing things:

    serial = spi(device=0,port=0,bus_speed_hz=1000000)
    device = sh1106(serial)
    

    They’ve been ticking along without mysterious blanking or mirroring for a bit over two weeks, so I’ll call it a fix.

  • Glass vs. Epoxy: Divot!

    The WS2812 RGB LED atop the Bowl of Fire Floodlight

    Reflector floodlight - purple phase
    Reflector floodlight – purple phase

    failed in the usual way after a bit over a year of constant use.

    I’d done an unusually good job of epoxying the ersatz heatsink in place:

    Reflector floodlight - finned LED holder
    Reflector floodlight – finned LED holder

    I wrapped the bulb in a towel with only the heatsink sticking out, whacked the side of the heatsink parallel to the glass with a plastic-face hammer, and it popped right off:

    Epoxy vs glass - divot
    Epoxy vs glass – divot

    The missing piece of the epoxy ring turned out to be a divot ripped out of the glass, which I didn’t notice until I’d chipped the fragment off the aluminum, so no pictures.

    Given the relative strengths of epoxy and glass, pulling a divot seems impossible, but folks doing 3D printing on glass platforms have been reporting exactly that failure for years. The prevailing theory seems to involve small scratches and defects in the glass surface, with subsequent weakening, and stresses applied to the epoxy perpendicular to the glass surface pulling the cracks apart.

    Replacing the RGB LED will require drilling it out and probably a complete rewiring, because I seem to have made liberal use of epoxy inside the heatsink and brass tube.

  • Streaming Media Player: OLED Weirdness

    Of late, the OLED displays on two RPi 3 streaming players (the others are RPi 2) have occasionally gone blank, while the players continue to work fine. I checked the logs, swapped MicroSD cards, rebuilt the images, and generally screwed around, all to no avail. The SH1106 controller has a command containing a single bit to blank the display and, perhaps, an SPI data transfer error could shut it off.

    This is much harder to explain:

    Mirror-image OLED display
    Mirror-image OLED display

    There’s a hardware command to flip the display top-to-bottom, not left-to-right. The Luma OLED driver can rotate the display in 90° increments, but AFAICT not reflect it.

    Yes, they’re networked, but, no, they’re not directly exposed to the Intertubes.

    Changing streams had no effect. Shutting down and rebooting restored normal operation.

    There’s been exactly one such failure so far, so I lack evidence.

    I have no clue what’s going on.

  • 60 kHz Preamp: Antenna Input Tickler

    The pair of air-wired inductors and resistors simulate the 60 kHz resonant loop antenna:

    60 kHz Preamp - antenna input tickler
    60 kHz Preamp – antenna input tickler

    I didn’t include a pair of capacitors to simulate the antenna’s stray capacitance, as the resonance is so broad and my signal so strong that pretty nearly anything will work.

    The current transformer injecting a differential signal into the “antenna” came without provenance; it measures 3 Ω DC resistance and 140 mH inductance, so it most likely has a bazillion turns. A 50 pF cap would resonate it at 60 kHz, where both reactances would be 53 kΩ, which also doesn’t matter.

    The alligator clips come from the LF Crystal Tester‘s buffer amp, with the Arduino Nano loaded with sine generator firmware. A 100 nF cap blocks the buffer’s DC bias, a 3.3 kΩ resistor (inside the heatstink tubing) knocks the signal down a bit, and it works pretty well:

    DDS ant drive - Preamp out
    DDS ant drive – Preamp out

    The upper trace is the DDS buffer output at the clip leads and the lower trace is the preamp output.

    The output of the LT1920 instrumentation amplifier (not shown) is 4.7 mVrms with a 20 dB gain, so the differential input runs a bit under 500 μV at the antenna terminals. That’s hotter than you’d expect from real WWVB RF, at least in the off season.

    Looking in the other direction, the net gain through LF353 bandpass filter and resonator works out to 22 dB, so the resonator isn’t all that lossy right in the middle of its passband. Yes, I’m cheating: I tweaked the series cap for maximum output at 60.000 kHz input.

    Good enough for a first pass, though, and makes for easy measurement.

  • 60 kHz Preamp: Filtering and Rebiasing

    The LT1920 instrumentation amplifier now sports two silver-mica caps on its inputs as a differential-mode input filter cutting back strong RF signals (clicky for more dots):

    60 kHz Preamp Schematic - DM filter inst amp - BP filter rebias - 2017-09-22
    60 kHz Preamp Schematic – DM filter inst amp – BP filter rebias – 2017-09-22

    In principle, a DM filter should eliminate RF rectification from out-of-band signals, although I think the attic is quiet enough to not need any help. The caps form a simple RC LP filter rolling off at 5.490 kΩ × 150 pF → 193 kHz, high enough above the 60 kHz signal to not make much difference down there.

    The silver-mica caps come from the Big Box o’ Caps, which contained an envelope with a few large 150 pF ±1% caps and a bag stuffed with similar 147 pF ±1% caps. Mixed in with the latter were some smaller 147 pF caps (*) of no particular tolerance (perhaps 5%), from which I neurotically matched a pair to 0.05 pF without too much effort. Doesn’t matter, given the other tolerances and suchlike, but it was amusing.

    I’d inadvertently grounded the cold end of the 330 Ω input resistor in the LM353 bandpass filter, now properly tied at the Vcc/2 virtual ground to take the DC load off the LT1920 output: a 100 nF cap (27 Ω at 60 kHz) stores the bias level without messing up the filter shape.

    A similar cap rebiases the protected resonator at the LT1010 buffer input:

    60 kHz Preamp Schematic - protected resonator - output rebias - 2017-09-22
    60 kHz Preamp Schematic – protected resonator – output rebias – 2017-09-22

    The new caps aren’t all that visible and the resonator vanishes in the clutter:

    60 kHz Preamp - protected resonator filter - overview
    60 kHz Preamp – protected resonator filter – overview

    Next: find out how well it works!

    (*) Yes, there were two envelopes between 150 pF and 147 pF:

    Silver-mica caps
    Silver-mica caps