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.

Tag: Improvements

Making the world a better place, one piece at a time

  • Raspberry Pi: OLED Display

    These cute displays have barely enough dots for the job:

    RPi OLED Display - Classical
    RPi OLED Display – Classical

    That’s a 0.96 inch = 24.4 mm OLED display, measured diagonally, with a breathtaking 8192 = 128×64 dots. It’s a binary display: on or off pixels, nothing in between. This is not a color display: what you see is what it does, with a two-pixel void between the yellow and blue sections.

    The void is a physical separation that does not affect the display addressing: the yellow section has 16 rows, the blue section has 48. It’s your responsibility to keep things where they belong; a character descender from the yellow section will appear in the blue section.

    They’re three bucks each, shipped halfway around the planet: search eBay / Amazon for oled 128x64 yellow. The all-blue and all-white versions do not have the two-pixel void. I have some white 1.3 inch versions on the way for those applications requiring 35% more visibility.

    The SPI interface uses all seven wires, peeled from a premade 100 mm 40-pin cable with female pin connectors:

    RPi OLED Display - Wiring
    RPi OLED Display – Wiring

    Other OLED versions have a four-wire I2C interface. The boards have option jumpers on the back, but the pin header along the edge will have 7 holes for SPI or 4 holes for I2C .

    Caveat emptor for online buyers: the item picture(s) may not match the title or the description text. The low-end sellers carrying beach balls, cookware, MOSFETs, cheap consumer electronics, and OLEDs do not understand the tech on a small board that’s Just Another SKU among thousands.

    For cables, search eBay or Amazon for ribbon dupont "female to female" 10cm. Amazon has sets of male-female, male-male, and female-female jumpers for ten bucks in various lengths. The insulation seems rather stiff and I may be forced to build better cables with fine wire inside PET braid.

    The SPI interface soaks up a tidy block of pins on the RPi’s big header:

    RPi OLED Display - RPi connector detail
    RPi OLED Display – RPi connector detail

    The LUMA-OLED Python driver doc gives a useful summary of those connections, herewith extracted for future reference:

    • 17 VCC – 3.3 V works for sure, 5 V might not
    • 18 DC – Data/Command
    • 19 D1 (“dee one”) – Data to display = MOSI
    • 20 GND
    • 21 not used, that’s the pin in the midst of the block
    • 22 RST – Reset
    • 23 D0 (“dee zero”) – clock to display = SCLK
    • 24 CS – Chip Select = CE0 (“cee ee zero”)

    Pin 1 is in front on the left end of that picture, closest to the MicroSD card slot, and proceeds 1-2, 3-4, and so forth along the length of the connector: odds toward the CPU, evens toward the PCB edge.

    The LUMA-OLD maintainter must have OLED boards with a slightly different SPI pinout than mine: VCC and GND are interchanged. Caveat emptor!

    Obviously, it’s desperately in need of a cute little case, which is in the nature of fine tuning.

     

  • SK2812 RGBW LED: Test Fixture

    [Edit: The SK2812 in the title and elsewhere should be SK6812. If I change the title, then all the other links break. So it goes.]

    An envelope of RGBW LEDs, allegedly with SK6812 controllers, arrived from halfway around the planet:

    SK2812RGBW LEDs - as received
    SK2812RGBW LEDs – as received

    The yellow phosphor sauce poured atop the blue LED on the left that makes it glow white leaves the upper loop of two wire bonds sticking out, but I can’t fault ’em for that. The overall build quality looks better than the ill-fated WS2812 LEDs, although it’s hard to tell by looking.

    I conjured a test stand from the vasty digital deep by tweaking the WS2812 mount:

    SK6812 LED Array Test Fixture - Slic3r preview
    SK6812 LED Array Test Fixture – Slic3r preview

    Wiring up a 5×5 panel went as before:

    SK2812RGBW test fixture - rear
    SK2812RGBW test fixture – rear

    The array test code adds another pixel channel and runs another raised sine wave with another random period, accomplished without much hackage.

    With the warm-white LED at full throttle (MaxPWM = 255), the panel tends toward the pallid end of HSV space:

    SK2812RGBW test fixture - front - W PWM255
    SK2812RGBW test fixture – front – W PWM255

    Dialing the white MaxPWM back to 32 crisps things a bit:

    SK2812RGBW test fixture - front - W PWM32
    SK2812RGBW test fixture – front – W PWM32

    Of course, the RGBW data stream isn’t compatible with the RGB data stream, so vacuum tubes with SK6812 chips require a slightly different driver and I can’t mix the two chips on a single tube.

    The Arduino source code as a GitHub Gist:

    // SK6812 RGBW LED array exerciser
    // Ed Nisley – KE4ANU – February 2017
    #include <Adafruit_NeoPixel.h>
    //———-
    // Pin assignments
    const byte PIN_NEO = A3; // DO – data out to first Neopixel
    const byte PIN_HEARTBEAT = 13; // DO – Arduino LED
    //———-
    // Constants
    #define UPDATEINTERVAL 20ul
    const unsigned long UpdateMS = UPDATEINTERVAL – 1ul; // update LEDs only this many ms apart minus loop() overhead
    // number of steps per cycle, before applying prime factors
    #define RESOLUTION 100
    // phase difference between LEDs for slowest color
    #define BASEPHASE (PI/16.0)
    // LEDs in each row
    #define NUMCOLS 5
    // number of rows
    #define NUMROWS 5
    #define NUMPIXELS (NUMCOLS * NUMROWS)
    #define PINDEX(row,col) (row*NUMCOLS + col)
    //———-
    // Globals
    // instantiate the Neopixel buffer array
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN_NEO, NEO_GRBW + NEO_KHZ800);
    uint32_t FullWhite = strip.Color(255,255,255,255);
    uint32_t FullOff = strip.Color(0,0,0,0);
    struct pixcolor_t {
    byte Prime;
    unsigned int NumSteps;
    unsigned int Step;
    float StepSize;
    float TubePhase;
    byte MaxPWM;
    };
    // colors in each LED
    enum pixcolors {RED, GREEN, BLUE, WHITE, PIXELSIZE};
    struct pixcolor_t Pixels[PIXELSIZE]; // all the data for each pixel color intensity
    unsigned long MillisNow;
    unsigned long MillisThen;
    //– Figure PWM based on current state
    byte StepColor(byte Color, float Phi) {
    byte Value;
    Value = (Pixels[Color].MaxPWM / 2.0) * (1.0 + sin(Pixels[Color].Step * Pixels[Color].StepSize + Phi));
    // Value = (Value) ? Value : Pixels[Color].MaxPWM; // flash at dimmest points
    // printf("C: %d Phi: %d Value: %d\r\n",Color,(int)(Phi*180.0/PI),Value);
    return Value;
    }
    //– Helper routine for printf()
    int s_putc(char c, FILE *t) {
    Serial.write(c);
    }
    //——————
    // Set the mood
    void setup() {
    pinMode(PIN_HEARTBEAT,OUTPUT);
    digitalWrite(PIN_HEARTBEAT,LOW); // show we arrived
    Serial.begin(57600);
    fdevopen(&s_putc,0); // set up serial output for printf()
    printf("WS2812 / SK6812 array exerciser\r\nEd Nisley – KE4ZNU – February 2017\r\n");
    /// set up Neopixels
    strip.begin();
    strip.show();
    // lamp test: run a brilliant white dot along the length of the strip
    printf("Lamp test: walking white\r\n");
    strip.setPixelColor(0,FullWhite);
    strip.show();
    delay(250);
    for (int i=1; i<NUMPIXELS; i++) {
    digitalWrite(PIN_HEARTBEAT,HIGH);
    strip.setPixelColor(i-1,FullOff);
    strip.setPixelColor(i,FullWhite);
    strip.show();
    digitalWrite(PIN_HEARTBEAT,LOW);
    delay(250);
    }
    strip.setPixelColor(NUMPIXELS – 1,FullOff);
    strip.show();
    delay(250);
    // fill the array, row by row
    printf(" … fill\r\n");
    for (int i=NUMROWS-1; i>=0; i–) { // for each row
    digitalWrite(PIN_HEARTBEAT,HIGH);
    for (int j=NUMCOLS-1; j>=0 ; j–) {
    strip.setPixelColor(PINDEX(i,j),FullWhite);
    strip.show();
    delay(100);
    }
    digitalWrite(PIN_HEARTBEAT,LOW);
    }
    // clear to black, column by column
    printf(" … clear\r\n");
    for (int j=NUMCOLS-1; j>=0; j–) { // for each column
    digitalWrite(PIN_HEARTBEAT,HIGH);
    for (int i=NUMROWS-1; i>=0; i–) {
    strip.setPixelColor(PINDEX(i,j),FullOff);
    strip.show();
    delay(100);
    }
    digitalWrite(PIN_HEARTBEAT,LOW);
    }
    delay(1000);
    // set up the color generators
    MillisNow = MillisThen = millis();
    printf("First random number: %ld\r\n",random(10));
    Pixels[RED].Prime = 3;
    Pixels[GREEN].Prime = 5;
    Pixels[BLUE].Prime = 7;
    Pixels[WHITE].Prime = 11;
    printf("Primes: (%d,%d,%d,%d)\r\n",
    Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime,Pixels[WHITE].Prime);
    unsigned int PixelSteps = (unsigned int) ((BASEPHASE / TWO_PI) *
    RESOLUTION * (unsigned int) max(max(max(Pixels[RED].Prime,Pixels[GREEN].Prime),Pixels[BLUE].Prime),Pixels[WHITE].Prime));
    printf("Pixel phase offset: %d deg = %d steps\r\n",(int)(BASEPHASE*(360.0/TWO_PI)),PixelSteps);
    Pixels[RED].MaxPWM = 255;
    Pixels[GREEN].MaxPWM = 255;
    Pixels[BLUE].MaxPWM = 255;
    Pixels[WHITE].MaxPWM = 32;
    for (byte c=0; c < PIXELSIZE; c++) {
    Pixels[c].NumSteps = RESOLUTION * (unsigned int) Pixels[c].Prime;
    Pixels[c].Step = (3*Pixels[c].NumSteps)/4;
    Pixels[c].StepSize = TWO_PI / Pixels[c].NumSteps; // in radians per step
    Pixels[c].TubePhase = PixelSteps * Pixels[c].StepSize; // radians per tube
    printf("c: %d Steps: %5d Init: %5d",c,Pixels[c].NumSteps,Pixels[c].Step);
    printf(" PWM: %3d Phi %3d deg\r\n",Pixels[c].MaxPWM,(int)(Pixels[c].TubePhase*(360.0/TWO_PI)));
    }
    }
    //——————
    // Run the mood
    void loop() {
    MillisNow = millis();
    if ((MillisNow – MillisThen) > UpdateMS) {
    digitalWrite(PIN_HEARTBEAT,HIGH);
    unsigned int AllSteps = 0;
    for (byte c=0; c < PIXELSIZE; c++) { // step to next increment in each color
    if (++Pixels[c].Step >= Pixels[c].NumSteps) {
    Pixels[c].Step = 0;
    printf("Color %d steps %5d at %8ld delta %ld ms\r\n",c,Pixels[c].NumSteps,MillisNow,(MillisNow – MillisThen));
    }
    AllSteps += Pixels[c].Step; // will be zero only when all wrap at once
    }
    if (0 == AllSteps) {
    printf("Grand cycle at: %ld\r\n",MillisNow);
    }
    for (int k=0; k < NUMPIXELS; k++) { // for each pixel
    byte Value[PIXELSIZE];
    for (byte c=0; c < PIXELSIZE; c++) { // … for each color
    Value[c] = StepColor(c,-k*Pixels[c].TubePhase); // figure new PWM value
    // Value[c] = (c == RED && Value[c] == 0) ? Pixels[c].MaxPWM : Value[c]; // flash highlight for tracking
    }
    uint32_t UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE],Value[WHITE]);
    strip.setPixelColor(k,UniColor);
    }
    strip.show();
    MillisThen = MillisNow;
    digitalWrite(PIN_HEARTBEAT,LOW);
    }
    }

  • ShopVac Hose Barb Adapter

    A small ShopVac arrived with a ribbed hose carrying an absurdly long wand, so I conjured a barbed adapter with a much shorter tapered snout for the machine tools:

    Vacuum hose fittings - hose barb to nozzle
    Vacuum hose fittings – hose barb to nozzle

    Trimming the hose end at one of the ribs makes a tidy fit:

    Vacuum hose fittings - ribbed hose barb
    Vacuum hose fittings – ribbed hose barb

    Now I need not trip over the vacuum hose between the bandsaw bench and the sander bench…

    The OpenSCAD code as a GitHub Gist:

    // Vacuum Hose Fittings
    // Ed Nisley KE4ZNU July 2016
    // March 2017
    Layout = "HoseBarb"; // PVCtoHose ExpandRing PipeToPort FVacPipe FVacFitting HoseBarb
    //- Extrusion parameters must match reality!
    // Print with 2 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    Protrusion = 0.1; // make holes end cleanly
    //———————-
    // Dimensions
    ID = 0;
    OD = 1;
    LENGTH = 2;
    VacNozzle = [30.1,31.8,30.0]; // nozzle on vacuum hose (taper ID to OD over length)
    MINOR = 0;
    MAJOR = 1;
    PITCH = 2;
    FORM_OD = 3;
    HoseThread = [32.0,(37.0 + HoleWindage),4.25,(1.8 + 0.20)]; // vacuum hose thread
    NumSegments = 64; // .. number of cylinder approximations per turn
    $fn = NumSegments;
    ThreadLength = 4 * HoseThread[PITCH];
    ScrewOAL = ThreadLength + HoseThread[PITCH];
    WallThick = 2.5;
    echo(str("Pitch dia: ",HoseThread[MAJOR]));
    echo(str("Root dia: ",HoseThread[MAJOR] – HoseThread[FORM_OD]));
    echo(str("Crest dia: ",HoseThread[MAJOR] + HoseThread[FORM_OD]));
    //———————-
    // Wrap cylindrical thread segments around larger plug cylinder
    module CylinderThread(Pitch,Length,PitchDia,ThreadOD,PerTurn,Chirality = "Left") {
    CylFudge = 1.02; // force overlap
    ThreadSides = 6;
    RotIncr = 1/PerTurn;
    PitchRad = PitchDia/2;
    Turns = Length/Pitch;
    NumCyls = Turns*PerTurn;
    ZStep = Pitch / PerTurn;
    HelixAngle = ((Chirality == "Left") ? -1 : 1) * atan(Pitch/(PI*PitchDia));
    CylLength = CylFudge * (PI*(PitchDia + ThreadOD) / PerTurn) / cos(HelixAngle);
    for (i = [0:NumCyls-1]) {
    Angle = ((Chirality == "Left") ? -1 : 1) * 360*i/PerTurn;
    translate([PitchRad*cos(Angle),PitchRad*sin(Angle),i*ZStep])
    rotate([90+HelixAngle,0,Angle]) rotate(180/ThreadSides)
    cylinder(r1=ThreadOD/2,
    r2=ThreadOD/(2*CylFudge),
    h=CylLength,
    center=true,$fn=ThreadSides);
    }
    }
    //– PVC fitting to vacuum hose
    module PVCtoHose() {
    Fitting = [34.0,41.0,16.0]; // 1 inch PVC elbow
    Adapter = [HoseThread[MAJOR],(Fitting[OD] + 2*WallThick + HoleWindage),(ScrewOAL + Fitting[LENGTH])]; // dimensions for entire fitting
    union() {
    difference() {
    cylinder(d=Adapter[OD],h=Adapter[LENGTH]); // overall fitting
    translate([0,0,-Protrusion]) // remove thread pitch dia
    cylinder(d=HoseThread[MAJOR],h=(ScrewOAL + 2*Protrusion));
    translate([0,0,(ScrewOAL – Protrusion)]) // remove PVC fitting dia
    cylinder(d=(Fitting[OD] + HoleWindage),h=(Fitting[LENGTH] + 2*Protrusion));
    }
    translate([0,0,HoseThread[PITCH]/2]) // add the thread form
    CylinderThread(HoseThread[PITCH],ThreadLength,HoseThread[MAJOR],HoseThread[FORM_OD],NumSegments,"Left");
    }
    }
    //– Expander ring from small OD to large ID PVC fittings
    // So a small elbow on the bandsaw fits into the hose adapter, which may not be long-term useful
    module ExpandRing() {
    Fitting_L = [34.0,41.0,16.0]; // 1 inch PVC pipe elbow
    Fitting_S = [26.8,32.8,17]; // 3/4 inch PVC elbow
    difference() {
    cylinder(d1=Fitting_L[OD],d2=(Fitting_L[OD] – HoleWindage),h=Fitting_L[LENGTH]); // overall fitting
    translate([0,0,-Protrusion])
    cylinder(d=(Fitting_S[OD] + HoleWindage),h=(Fitting_L[LENGTH] + 2*Protrusion));
    }
    }
    //– 1 inch PVC pipe into vacuum port
    // Stick this in the port, then plug a fitting onto the pipe section
    module PipeToPort() {
    Pipe = [26.5,33.5,20.0]; // 1 inch Schedule 40 PVC pipe
    difference() {
    union() {
    cylinder(d=Pipe[OD],h=(Pipe[LENGTH] + Protrusion));
    translate([0,0,(Pipe[LENGTH] – Protrusion)])
    cylinder(d1=VacNozzle[OD],d2=VacNozzle[ID],h=VacNozzle[LENGTH]);
    }
    translate([0,0,-Protrusion])
    cylinder(d=Pipe[ID],h=(Pipe[LENGTH] + VacNozzle[LENGTH] + 2*Protrusion));
    }
    }
    //– Female Vac outlet inside PVC pipe
    // Plug this into PVC fitting, then plug hose + nozzle into outlet
    module FVacPipe() {
    VacPort = [30.0,31.3,25]; // vacuum port on belt sander (taper ID to OD over length)
    Pipe = [26.5,33.5,20.0]; // 1 inch Schedule 40 PVC pipe
    difference() {
    cylinder(d=Pipe[OD],h=VacPort[LENGTH]);
    translate([0,0,-Protrusion])
    cylinder(d1=VacPort[ID],d2=VacPort[OD],h=(VacPort[LENGTH] + 2*Protrusion));
    }
    }
    //– Female Vac outlet on 3/4 inch fitting OD
    // Jam this onto OD of fitting, plug hose + nozzle into outlet
    module FVacFitting() {
    Adapter = [26.5,(33.5 + 2*WallThick),17.0]; // overall adapter
    //VacPort = [30.0,31.3,25]; // vacuum port on belt sander (taper ID to OD over length)
    VacPort = [30.1,31.8,30.0]; // vacuum port for bandsaw = inverse of hose nozzle
    Fitting = [26.8,32.8,17]; // 3/4 inch PVC elbow
    TaperLength = 5.0; // inner taper to avoid overhang
    difference() {
    cylinder(d=Adapter[OD],h=Adapter[LENGTH]); // overall fitting
    translate([0,0,-Protrusion])
    cylinder(d=(Fitting[OD] + HoleWindage),h=(Adapter[LENGTH] + 2*Protrusion));
    }
    translate([0,0,Adapter[LENGTH]])
    difference() {
    cylinder(d=Adapter[OD],h=TaperLength);
    translate([0,0,-Protrusion])
    cylinder(d1=(Fitting[OD] + HoleWindage),d2=VacPort[ID],h=(TaperLength + 2*Protrusion));
    }
    translate([0,0,(TaperLength + Adapter[LENGTH])]) // vac fitting
    difference() {
    cylinder(d=Adapter[OD],h=VacPort[LENGTH]);
    translate([0,0,-Protrusion])
    cylinder(d1=VacPort[ID],d2=VacPort[OD],h=(VacPort[LENGTH] + 2*Protrusion));
    }
    }
    //– Hose barb to male vacuum taper
    module HoseBarb() {
    HoseFitting = [29.0,32.2,38.5];
    Barb = [HoseFitting[OD],35.5,4.0];
    BarbOffset = 17.0;
    Seat = [HoseFitting[OD],36.0,5.0];
    SeatSupport = [HoseFitting[OD],Seat[OD],(Seat[OD] – HoseFitting[OD])/2];
    OAL = HoseFitting[LENGTH] + SeatSupport[LENGTH] + Seat[LENGTH] + VacNozzle[LENGTH];
    NumSides = 4*8;
    difference() {
    union() {
    cylinder(d=HoseFitting[OD],h=HoseFitting[LENGTH],$fn=NumSides);
    translate([0,0,BarbOffset])
    cylinder(d1=Barb[ID],d2=Barb[OD],h=Barb[LENGTH],$fn=NumSides);
    translate([0,0,HoseFitting[LENGTH]])
    cylinder(d1=SeatSupport[ID],d2=SeatSupport[OD],h=SeatSupport[LENGTH],$fn=NumSides);
    translate([0,0,HoseFitting[LENGTH] + SeatSupport[LENGTH]])
    cylinder(d=Seat[OD],h=Seat[LENGTH],$fn=NumSides);
    translate([0,0,HoseFitting[LENGTH] + SeatSupport[LENGTH] + Seat[LENGTH]])
    cylinder(d1=VacNozzle[OD],d2=VacNozzle[ID],h=VacNozzle[LENGTH],$fn=NumSides);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=HoseFitting[ID],d2=(VacNozzle[ID] – 10*ThreadWidth),h=OAL + 2*Protrusion,$fn=NumSides);
    }
    }
    //———-
    // Build things
    if (Layout == "PVCtoHose")
    PVCtoHose();
    if (Layout == "ExpandRing") {
    ExpandRing();
    }
    if (Layout == "PipeToPort") {
    PipeToPort();
    }
    if (Layout == "FVacPipe") {
    FVacPipe();
    }
    if (Layout == "FVacFitting") {
    FVacFitting();
    }
    if (Layout == "HoseBarb") {
    HoseBarb();
    }
  • Streaming Radio Advertisements: Carpet Bombing

    After a protracted silence in a Radionomy stream, the Raspberry Pi player offered this log:

    2017-03-05 11:17:07,890 INFO: Starting mplayer on Plenitude -> /home/pi/Playlists/Radio-PLENITUDE.m3u
    2017-03-05 11:17:13,651 INFO: Track name: []
    2017-03-05 11:44:02,296 INFO: Track name: [David Wahler - Whispers from Eternity]
    2017-03-05 11:46:36,995 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 11:47:07,117 INFO: Track name: []
    2017-03-05 11:49:07,080 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 11:49:10,079 INFO: Track name: [Jef Mounet & Danièle Mounet - L'ancre musicale Natures d'Eau]
    2017-03-05 12:02:02,271 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:02:32,424 INFO: Track name: []
    2017-03-05 12:04:32,243 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:05:01,925 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:07:02,276 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:07:31,968 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:09:32,262 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:10:02,192 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:12:02,311 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:12:32,184 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:14:32,085 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:15:02,217 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:17:02,057 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:17:32,445 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:19:32,083 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 12:19:35,171 INFO: Track name: [Jean-Marc Staehle - Bercé par tant de beauté]
    2017-03-05 12:23:42,410 INFO: Track name: [Francesco - Sur le chemin]
    2017-03-05 12:29:50,265 INFO: Track name: [Michel Pépé - Pacifica]
    2017-03-05 12:35:07,493 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:35:37,377 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:37:37,478 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 12:37:41,476 INFO: Track name: [Music And Wellness (Musique Et Bien Etre) - Absolute Winner]
    2017-03-05 12:46:36,742 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:47:06,668 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:49:06,538 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 12:49:10,270 INFO: Track name: [Patrick Vuillaume &Nicole Bally - Pearls of Light (Instrumental by Nicole Bally)]
    2017-03-05 12:53:45,357 INFO: Track name: [Trine Opsahl - Sister moon]
    2017-03-05 12:54:58,596 INFO: Track name: [Peter Kater - Rebirth]
    2017-03-05 13:04:52,726 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 13:05:22,665 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 13:07:21,561 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 13:07:25,808 INFO: Track name: [Deuter - Flowing]
    2017-03-05 13:12:55,970 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 13:13:25,859 INFO: Track name: []
    2017-03-05 13:15:26,449 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 13:15:33,022 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 13:15:59,437 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 13:17:59,559 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 13:18:06,133 INFO: Track name: [O - Part I]
    

    The Jingle lines introduce a short interlude of chimes separating music from advertisements. The Intro chimes play for 30 seconds and the Extro chimes play for three to five seconds. Some stations have similar interludes, others do not; apparently the station gets to choose the format.

    The [Targetspot - TargetSpot] lines mark two minutes of TargetSpot insertion: either advertisements (if you’re in their target market) or generic musical interludes similar to the station’s genre (if you’re out-of-market). The ads and music often lack volume-matching with the streaming music, rarely have lower volume, and the ads are incomprehensible to my ears. The musical interludes seem to be randomly chosen from a small set of candidate tracks that, along with the chimes, become annoyingly familiar in short order.

    The [] lines (yes, an empty string) mark two minutes of Public Service Announcements, advertisements, or generic musical interludes. I’m uncertain how they differ from the [Targetspot - TargetSpot] insertions.

    At a minimum, Radionomy inserts two minutes of TargetSpot / PSAs after every 12 to 15 minutes of music. Adding in the Jingle markers, ads occupy just under 20% of the total “airtime” for this station.

    However, bizarre events like the 17 nonstop minutes of jingles and ads inserted just after noon occur with inexplicable frequency. I’ve noticed half an hour of similar back-to-back-to-back ads on other stations, so it’s not a rare event.

    To quote the TargetSpot website:

    TargetSpot serves ads in real time to each listener’s personalized stream, creating a one-to-one relationship between the advertiser and the listener. The result is a dramatic increase in message relevancy and campaign effectiveness

    Those keyword markers turn out to be incredibly convenient. Just sayin’…

  • Epoxy Mixing Pads

    Quilters hold fabric in place with freezer paper while piecing their blocks; it’s basically plastic-coated paper that gets tacky at ordinary clothes iron temperatures.

    It’s useful in the shop, too. Cut a length of freezer paper into small pages, pad them plastic-side-up atop a sheet of cardboard, and you get a great place to mix small amounts of epoxy:

    Epoxy mixing pad
    Epoxy mixing pad

    Let the pad stay next to whatever you’re epoxying (like, say, the lathe tailstock ways), then test the leftover epoxy for hardness… rather than messing up the joint you so laboriously created by moving the parts an hour too soon.

    Works for me, anyhow. Highly recommended!

  • Mini-Lathe Carriage Stop: Spring Counterbore

    While pondering the tailstock ways, I realized the spring on the LMS Adjustable Carriage Stop just needed a counterbore to make it work right:

    LMS Carriage Stop - spring counterbore
    LMS Carriage Stop – spring counterbore

    The OEM spring now sits slightly compressed with the screw tip flush at the far end of the block:

    LMS Carriage Stop - reassembled
    LMS Carriage Stop – reassembled

    That OEM screw head knurling leaves a bit to be desired, doesn’t it?

    Actually boring the hole would be a remarkably tedious process for little gain. Instead, I lined up the block in the drill press using a ¼ inch drill (the OEM hole isn’t hard metric!) in the unthreaded section, enlarged it with progressively larger drills up to an O (0.316 inch = 8 mm), then finished with a P (0.323 in = 8.2 mm).

    As it turned out, my guesstimated relaxed spring length was a bit off, so I turned a brass bushing to shorten the hole by 2 mm:

    LMS Carriage Stop - screw bushing
    LMS Carriage Stop – screw bushing

    If I don’t mention it, nobody will ever know!

    The original doodle, with close-enough sizes:

    LMS Carriage Stop - spring counterbore doodle
    LMS Carriage Stop – spring counterbore doodle
  • Monthly Science: Minimal-Woo Cast Iron Pan Seasoning

    After trying several variations on a theme, our daily-use pan now looks like this:

    Cast Iron Pan - after weekly seasoning
    Cast Iron Pan – after weekly seasoning

    Those obvious wiping marks come from an oily rag in a hot pan. What could go wrong?

    The reflected light bar comes from the under-cabinet LED strip.

    The surface withstands stainless utensils, cooks omelets with aplomb, and requires no fussy KP:

    Omelet in cast-iron pan
    Omelet in cast-iron pan

    The low-woo seasoning recipe, done maybe once a week when the bottom has more gunk than usual:

    • Clean the pan as usual, wipe dry
    • Begin heating on medium burner set to High
    • Add 0.2 ml = 10 drops = 1 squirt of flaxseed oil
    • Wipe around pan interior with small cotton cloth
    • Continue heating to 500 °F, about four minutes
    • Carefully wipe oily cloth around pan again
    • Let cool

    Works for us and doesn’t involve any magic.