Simplicate and add lightness

A Business Analyst at one of my old jobs came from the aerospace engineering world.  He said the engineers at his old place had this phrase, “Simplicate and add lightness.”

It sounds pretty silly, because “simplicate” isn’t a word, and if you “add” something, it generally makes it heavier.

But that’s what I’m doing right now.  I don’t actually need a custom domain for this blog, so by adding it to a hosted WordPress.com account, I can “simplicate”!

So, welcome to my new WordPress.com home, and goodbye to some annual domain renewal fees!

Advertisements

Too many projects!

I’m the kind of person who loves exploring new ideas. And, I enjoy the process of tinkering and learning. I do need a practical purpose for much of what I do, otherwise I tend to lose interest.

In the past few years, I’ve collected thousands of individual electronic parts, from the tiniest surface mount capacitors to complete single-board computers.  Thousands. And I have no idea where they are, or exactly how many of a particular part I have left.

So I’m putting most of my projects on hold while I build a massive database, so I can track and organize what I need, and when I need it.

And then I  can start building robots again!

Hacking DCC, Part 1

So, this is interesting.  I finally got a chance to use my oscilloscope for actual scientific purposes!

I figured it would be possible, but I wasn’t quite sure how, to use the ATMEGA328P’s timers to create two opposing square waves.  Turns out, it’s pretty easy to make one waveform, but to output both…

Actually, it turns out that’s pretty easy too.  All it takes is a FOC1B to make OC1B spin on a different cycle, 180° in opposition to OC1A.  OC1A and OC1B output on PB1 and PB2, respectively.

Square waves in opposition

Cool.  The pink signal is OC1A, and the green one is OC1B.  This picture is actually from a little bit later in the game, when I was manipulating the signal to send pulses of varying length.  The longer pulses represent 0, and the shorter ones represent 1.

What on Earth will happen when I hook up PB1 and PB2 to an H-bridge, and give it 15 volts or so?  It becomes a modulated AC signal, that can be used to transmit power and data down the same two wires! 🙂

But I did notice, after all this, that OC1B is a little bit delayed.  But I think I can live with a delta of 569 nanoseconds.

OC1B follows OC1A slowly

Next, I need to sort out how to disrupt the signal entirely, for approximately 454 microseconds.

Not for the faint of heart: here’s some code.  I do the JavaScript thing in my day job, Bitwise AVR register manipulation is a little bit… weird… if you ask me.

void startDcc() {
    // Toggle both OC1A and OC1B on compare match
    TCCR1A &= ~(_BV(COM1A1) | _BV(COM1B1));
    TCCR1A |= (_BV(COM1A0) | _BV(COM1B0));

    // Set waveform generation mode to CTC
    // Using OCR1A for comparison
    TCCR1A &= ~(_BV(WGM11) | _BV(WGM10));
    TCCR1B &= ~(_BV(WGM13));
    TCCR1B |= (_BV(WGM12));
    
    // Set clock source to 8x prescaler
    TCCR1B &= ~(_BV(CS12) | _BV(CS10));
    TCCR1B |= (_BV(CS11));

    // Force output compare on OC1B
    TCCR1C |= (_BV(FOC1B));
    
    // Set default waveform (string of 1's)
    OCR1A = BIT_1_LENGTH;
    
    // Set pin modes to output
    // PB2, PB1
    DDRB |= (_BV(DDB2) | _BV(DDB1));
    PORTB &= ~(_BV(PORTB2) | _BV(PORTB1));

    //Interrupts.
    TIMSK1 |= (_BV(ICIE1) | _BV(OCIE1A));
}

My First Raspberry Pi HAT

I’m a bit late in reporting on this, because much of the work happened over the Labour Day weekend, waiting in ferry lineups and such.  I spent too much time poring over datasheets, double- and triple-checking my design, and while I’m still not sure that it’ll work, I’m going ahead with the first revision of my first Raspberry Pi HAT.

My first HAT is intended to be a part of the Office Robot.  But I can see some other potential uses for it, so I tried to make the design a little more flexible.

Designed Features:

  • Mono microphone input
  • 3.7W stereo speaker output
  • SPI peripheral connector, with an interrupt and reset line
  • I2C peripheral connector, with shared interrupt and reset lines
  • 5V power input
  • Power control button

I am confident that the level-shifted peripheral connectors will work properly, as I’ve prototyped both on a breadboard.  It’s the audio stuff I’ve never played with before, that I’m nervous about.  But even if those bits don’t work properly, the HAT will still be a lot cleaner than the breadboard covered in jumper wires.

I’ve got my fingers crossed, but if I have to build Version 2, I’m okay with that, too.

 

Apartment living, and hobbies

It turns out, living in an apartment and trying to manufacture things, can be a bit of a challenge.  I’ve spent the last two days tiptoeing around a plastic painter’s dropsheet, upon which are pieces of trackbed for the Office Train.

I’ve been holding off on doing any further work on the train, since I’m told that a desk shuffle is imminent.  But then I remembered that I made all the pieces so that they’d match the length of the modular cubicle walls.  And as long as I install the flex track, cut to match, the trackbed pieces can be easily moved and adjusted!

But more importantly, I’ll finally get this pile of wood out of my cramped living quarters!

Measuring wheel speed

Each wheel on the robot is paired with a slotted disc, which passes through an optical sensor.  There are 20 slots on each disc, which translates to 20 “on” pulses per rotation.  However, it’s easier for me to count both the “on” and “off” pulse edges, so I’m dealing with 40 pulse edges per each wheel rotation.  I’m going to call these ticks as that’s a lot easier to say.
DSC_9248
But ultimately, I don’t care about ticks.  I do care about speed, though.  So I’m going to start there, and figure out what I need in order to calculate that.

Simply stated:

speed=frac{distance}{time}

or:

speed=frac{1 tick}{timeSinceLastTick}

or, if I throw some averaging in there:

speed=frac{numTicks}{elapsedTime}

I can already see that there’s going to be a problem when the speed approaches zero, as there are no ticks to measure the timings.  So I’m going to start by measuring both the number of ticks that have occurred, and the amount of time that’s passed.  If too much time has passed, I’ll consider the speed to be zero.

// Store up to 10 "tick" (or "no-tick") events
const uint8_t NUM_SAMPLES = 10;

// Keep track of how many of the "tick" or "no-tick" events have happened.
uint8_t _tickCount[NUM_SAMPLES];
volatile uint8_t _totalTicks = 0;

// Keep track of the event timestamps
uint32_t _timings[NUM_SAMPLES];

// Some pointers to the arrays above.
volatile uint8_t _oldestIndex = 0;
volatile uint8_t _newestIndex = NUM_SAMPLES - 1;

When a tick occurs, the system calls an interrupt:

ISR(PCINT1_vect) {
    uint8_t newState = PINC & (_BV(PINC2) | _BV(PINC3));
    uint64_t nowStamp = micros(); // timestamp in µs
    uint8_t changes = newState ^ _encoderState;
    _encoderState = newState;

    if (changes & (_BV(PINC2))) {
        _totalTicks = _totalTicks - _tickCount[_oldestIndex] + 1;
        _tickCount[_oldestIndex] = 1;
        _timings[_oldestIndex] = nowStamp;

        // Move the pointers along, overflowing back to zero if needed.
        _newestIndex = _oldestIndex;
        _oldestIndex++;
        if (_oldestIndex >= NUM_SAMPLES) {
            _oldestIndex = 0;
        }
    }
}

If a tick didn’t happen, we inject a zero into the mix:

// Called when... nothing happened!
void nothingHappened() {
    cli();
    // Overwrite the "oldest" item in the averaging loop, and adjust pointers.
    _totalTicks = _totalTicks - _tickCount[_oldestIndex] + 0;
    _tickCount[_oldestIndex] = 0; // nothing happened!
    _timings[_oldestIndex] = micros();

    _newestIndex = _oldestIndex;
    _oldestIndex++;
    if (_oldestIndexB >= NUM_SAMPLES) {
        _oldestIndexB = 0;
    }
    sei();
}

And finally, in order to calculate the current speed:

double getSpeed() {
    cli(); // Make sure the interrupt doesn't fire while we're in here.
    uint8_t totalTicks = _totalTicks;
    uint64_t oldestTime = _timings[_oldestIndex];
    uint64_t newestTime = _timings[_newestIndex];
    sei(); // Set the interrupts free!

    // To get the time difference, we can't simply subtract,
    // because micros() overflows every 70 minutes or so.
    // Implementation of getTimeDiff is left as an exercise
    // to the reader.
    uint64_t timeDiff = getTimeDiff(oldestTime, newestTime);

    if (totalTicks == 0 || timeDiff == 0 || timeDiff > 10000000) {
        return 0.0;
    } else {
        return (float)((totalTicks - 1) * 1000000) / (float)(timeDiff);
    }
}

There’s a little more to it, but you’ve got the idea.  It’s just a bunch of code that watches how far we’ve gone, another bunch that watches the clock, and at the end of the day, it’s just simple physics:

speed=frac{distance}{time}

Interface board gets some feature creep

Yes, the parts count keeps going up.

A board I’m working on that’ll sit atop a Raspberry Pi.  It’ll replace the mass of jumper wire that’s currently serving the same purpose.  This board is primarily for the robot, but I have a couple of other usecases in mind.  Besides, I get three copies of the board from the PCB house, so I might as well make it slightly multi-purpose.

Now it has an I2S audio codec chip.  I’ve never tried to do anything with digital audio before, so I’ll probably end up with a Version Two at some point in the future.

foo

So far, it’s got:

  • Yet another ATMEGA328p microcontroller.  This one could easily be something with less horsepower, as it’s just there to manage the power supply and try to make sure bad things don’t happen.
  • Connection for “Motion Controller” board
  • Connection for I2C peripherals (the camera pan/tilt and a few other accessory things will attach to this bus)
  • Microphone input
  • Speaker output
  • Power toggle button

I think I should go to bed though, before this gets too many more features!