eBay Mini-vacuum Pump

Quick version: Hey this little thing works and seems practical for robotics, specifically the Pop Can Challenge.

I had to add “mini” to the title of this post, calling this a vacuum pump seems odd when you’ve played around with big pumps. Which leads me to make a mental note to share some information my industrial vacuum pump sometime; my big pump is currently at the SnoCo Makerspace but it seems it gets used as hold-down weight on glue-ups more than it’s intended use but at least it’s getting some love.

I moved workshops; I swear this is related… The kids are at an age were it’s hard for me to get out to my workshop for very long and Melissa works in the evenings right now so I just wasn’t getting any fun-stuff done. I’ll shoot another workshop video… soon-ish. I’ve been slowly filtering through my bins of incomplete or never-started projects in and sorting through it all to bring some organization into my “inside workshop”. In digging through the bins I recently came across most of this eBay purchase: https://ebay.us/2L8SOa . If you want to skip on clicking the link it’s a small motor/vacuum pump with a suction cup grabber. I bought this for PopBot after the 2019 Robothon. I put this aside because I’ve been working on PopBot and a second marine-based robot platform for the last two months. PopBot is getting a deep drive because the SRS Robothon (2020) https://robothon.org/ has been cancelled this year. The thought is I might be able to grab the PopCan with a suction cup instead of actively grabbing it, or in my case I was going to sort hug it/lasso the pop can; time will tell on this. My initial concern is current draw, thankfully thing only pulls a couple hundred milliamps and then it’ll hold it’s suction fairly well for a while. I noticed a solid drop off in current after the suction was “successful”. I would have thought it opposite but I guess based on looking at current curves on squirrel cage fans and seeing them unload without air flow I’ll accept it without figuring out why. My goal is to grab the can with a pair of suction cups, drop the motor PWM or turn it off once the suction is maintained (as determined by current) and then just provide a couple moments of pumping to maintain suction while transporting the can. For testing I came up with a quick little circuit with a Pololu DC motor driver (#2961) and an Adafruit i2c IN219 current sensor breakout board… don’t mind all the other stuff strapped to the MEGA in the photo below. It was on my bench for some sensor fusion testing. The INA219 is hooked up to the i2c ports (20/21) on the MEGA2560 and the motor driver is getting a PWM drive from Pin 6. I didn’t need to use the enable or direction, I just sunk the enable low. The amount of suction is quite adequate if you’re a 14 year old boy trying to get popular by giving yourself a hickey…. this is not recommended of course and mostly a guess as I have 4-day-old vacation stubble so there is no sense in trying.

The code for testing is at the end of the post, anyone with a couple hours of experience with Arduino IDE could whip this out in 10 minutes but just in case you were hoping to save 9-1/2 minutes…

/*  @chasihler iradan.com
 * 
 * 0.1 2020/08/20 Test
 * 
 * INA219 test w/ Vacuum Pump
 * Most of this is from the adafruit library example. 
 * 
 */


#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

int motor_pwm_pin = 6;
int motor_enable_pin = 7;

int int_stop = 0;
int int_lowSpeed = 950;
int int_highSpeed = 1023;

void setup() {
  Serial.begin(115200);
    
  pinMode(motor_pwm_pin, OUTPUT);
  pinMode(motor_enable_pin, OUTPUT);

    if (! ina219.begin()) {
      Serial.println("Failed to find INA219 chip");
        while (1) { delay(10); }
    }
      //ina219.setCalibration_32V_1A();
      //ina219.setCalibration_16V_400mA();
}

void loop()  {
  float shuntvoltage = 0;
  float busvoltage = 0;
  float current_mA = 0;
  float loadvoltage = 0;
  float power_mW = 0;
  Serial.println("No Load Test");
  Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  Serial.println("");
  
  analogWrite(motor_pwm_pin, int_lowSpeed);
  delay(1000);

  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage + (shuntvoltage / 1000);

  Serial.println("Low Speed Test");
  Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  Serial.println("");
    
  delay(1000);
  analogWrite(motor_pwm_pin, int_stop);  //stop
  delay(5000);
  analogWrite(motor_pwm_pin, int_highSpeed);
  delay(1000);

  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage + (shuntvoltage / 1000);
  
  Serial.println("Full Speed Test");
  Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  Serial.println(""); 
     
  delay(2000);
  analogWrite(motor_pwm_pin, int_stop);  //stop
  delay(10000);
}

Programming ESC with Arduino

The story: I hit a break wall with ODrive. ODrive is an open source brushless DC motor (BLDC) driver built with robotics in mind (my application..). I don’t have faith in the project and after months of trying to make it fit my application I’m abandoning it. It’s painful to spend that kind of money on a product (two even!) … but I’m not hopping back on the Stepper bandwagon just yet. I’ll see if I can put my brushless DC motors to use on PopBot 0.5.

First step was deciding on if I could use an off the shelf ESC or buy one. I purchased a RC “rockcrawler” driver as I assumed the firmware would be most compatible with my application. The car and boat ESCs have forward and reverse speed by splitting the PWM servo input in half. While waiting on shipping I found an ESC for a boat and a few for quads in one robot junk bins. Quads are single directions but the boat one was good enough for testing. It had no torque in the low end but that’s not a surprise based on the application with the motor type. I needed a way to program this ESC without a controller and receiver. Programming is typically performed w/ the receiver/controller by pushing the speed to min-max in a certain order. Well that can be done with the arduino of course. Here is a super simple piece of code for you if you don’t want to reinvent the wheel. This will also allow you to control your ESC once it’s programmed and you can uncomment my basic acceleration code if desired.

Hook up the ESC control signals (servo input). On the ESC the white (signal in, sometimes I think the alternate color is orange?) to digital output 9 on the Arduino Uno or Mega. Tie the black (or brown) wire to one of the grounds. DO NOT HOOK UP RED. We don’t need the power from the ESC.. it’s usually not something we want anyways (like 6V… ).

You’ll need this library. Download it and place the uncompressed directory in your Arduino libraries directory.

https://github.com/maxpowel/ESC

/*
 * Charles Ihler, iradan.com
 * 2020-04-27 build from example of library. 
 * 
 * Open the serial monitor... enter 0 (and enter) for reverse
 * 1 for foward and 2 for idle/stop.. 
 * Most ESCs want a motor hooked up or warn of damage.. make sure the motor is safe and can't spin off and hurt something. 
 * 
 */

#include <Servo.h>
#include "ESC.h"



ESC esc(ESC::MODE_FORWARD_BACKWARD);


int sel = 0;
String ssel;

void setup() 
{
  Serial.begin(115200);  
  pinMode(LED_BUILTIN, OUTPUT);
  esc.attach(9); //change to some other PWM pin if required

} 

void output_high() {
   digitalWrite(LED_BUILTIN, HIGH);
   esc.setDirection(ESC::FORWARD);
   esc.setSpeed(500);   //comment this out and uncomment below for acceleration. When test running motor.
   //for (int i = 0; i <= 150; i++) {
   //   esc.setSpeed(i);
   //delay(25);
   //}
}
void output_low() {
  digitalWrite(LED_BUILTIN, LOW);
  esc.setDirection(ESC::BACKWARD);
  esc.setSpeed(500);   //comment this out and uncomment below for acceleration. When test running motor.
  //  for (int i = 0; i <= 150; i++) {
  //    esc.setSpeed(i);
  //  delay(25);
  //}
}
void output_n() {
  digitalWrite(LED_BUILTIN, LOW);
  esc.setDirection(ESC::BACKWARD);
  esc.setSpeed(0);
}
void loop() 
{
    Serial.print("Forward (1), Reverse (0), Stop/N(2)?(1/0/2): "); 
    while (Serial.available() == 0) {}  
    ssel = Serial.readString();  
    Serial.print(" --> ");
    Serial.println(ssel);
    sel = ssel.toInt();
    if (sel == 1) {
      output_high();
      Serial.println("ON!");
    }
    if (sel == 0) {
      output_low();
      Serial.println("OFF.");
    }
    if (sel == 2) {
      output_n();
      Serial.println("Neutral");
    }
  delay(100);

} 
The setup project with motor removed.

Enjoy…

ESP32 Boot Camp

… for me.

I got a rando e-mail offering me “free parts” and I usually don’t even bother opening the e-mail. It’s ALWAYS garbage right? Well, maybe I hadn’t slept well the night before but I opened it. (psss. You can scroll down about a page before I actually get anywhere in this post…)

What was I thinking? I hovered carefully over the link… seemed pretty safe… short-ish domain ended with a .com .. not extra stuff on the end… OK. They got me. The website was http://digitspace.com — It seemed like an electronics website so I e-mailed. Back…. going back and forth they seem to be running some kind of promotion or perhaps they are trying to build some brand recognition getting bloggers to give them some advertising. In the end selected $50 worth of parts that consisted of two ESP32 micro dev boards that had WiFi (of course), LoRa, OLED display and and extra LED. There was some switch button types but they were a bit more and for some reason I didn’t think I needed them. Well a week later these things actually showed up (and I didn’t have to give them my credit card or paypal info!).

OK THE START..

A week later the boards showed up in the mail.. they look and feel reasonable and they came with some sample firmware that proved they worked. Unfortunately the site did not include any documentation… but I did the “hardwork” for you if you want to follow along… but let’s start with the end… So far I am here:

First thing first I had to add ESP32 support to my Arduino IDE.. I dropped this in the “additional board manager URLs”:

https://dl.espressif.com/dl/package_esp32_index.json

See how in this quick video..

I found a simple tx-rx LoRa code off the internet but I won’t share because it took too much re-work to get working on my board. So I took it’s essience …

I had to add these libraries:

Add libraries:
Adafruit SSD1306 by Adafruit
Adafruit GFX Library by Adafruit
LoRa by Sandeep Mistry

(all came right off the normal library manager)

I couldn’t find an OLED library that worked until reading some and found I liked this one that seemed to be the “best for me”:
https://github.com/osresearch/esp32-ttgo

After that it was smooth sailing. My code is work-in-progress towards my final project. It can be found here: https://github.com/chasihler/TTGO-OLED-RX-TX-HTTP-PUT

Inspiration along the way:

Battery Monitoring: https://github.com/YogoGit/TTGO-LORA32-V1.0

A nice post, read too late, had the correct pinout/board of many different but similar boards: http://brettbeeson.com.au/ttgo-lora-esp32-v2-1-r1-6/

Putting your ESP32 to sleep:
https://randomnerdtutorials.com/esp32-deep-sleep-arduino-ide-wake-up-sources/

My findings were similar … though I haven’t ACCURATELY measured it my USB analyzer says 50mA on and 10mA off.. no peak readings yet but I can live with 10mA in my project.

MODbus/RTU on a PIC Microcontroller

By request one of my long lost blogs was a MODbus RTU application for a PIC 18F27J53. You could easily plop this onto many of the 18F series micros. It doesn’t do much but it’s a great stepping stone for throwing in your own IO. I’ll probably follow up with an Arduino version in a while.. I’ll put it on my “todo” list.

The code…

/*
 File:   main.c
 Author: Charles M Ihler
 Contact at: http://iradan.com
 *
 Created on November 8, 2014, 2:37 PM
 *
 Target Device:
 18F27J53 Development Board by AtomSoft
 *
 Project: MODbus/RTU slave
 *
 Notes
 We will ignore both query high bytes for now
 TODO:   Pull all hard coded IO out eventually and set it up via USB?
 Maybe a terminal application for enabling points?
 *
 *
 Version:
 0.02     Controlled sends caned reply on request to address on RS-232
 0.03     CRC works on RX
 0.04     No actual IO yet..
 Function 0x02 (Read Input Status) Started
 0.05     Ported to 18F17J53 for expanded IO/USB options.
 12MHz XTAL with CPUDIV set to 16MHz
 *
 / //#ifndef _XTAL_FREQ //#define _XTAL_FREQ 16000000 // //#define __delay_us(x) _delay((unsigned long)((x)(_XTAL_FREQ/4000000.0)))
 //#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
 //#endif 
 // PIC18F27J53 Configuration Bit Settings
 // 'C' source line config statements
 include 
 // #pragma config statements should precede project file includes.
 // Use project enums instead of #define for ON and OFF.
 // CONFIG1L
 pragma config WDTEN = OFF      // Watchdog Timer (Disabled - Controlled by SWDTEN bit)
 pragma config PLLDIV = 3       // PLL Prescaler Selection (Divide by 3 (12 MHz oscillator input))
 pragma config CFGPLLEN = ON    // PLL Enable Configuration Bit (PLL Enabled)
 pragma config STVREN = ON      // Stack Overflow/Underflow Reset (Enabled)
 pragma config XINST = OFF       // Extended Instruction Set (Enabled)
 // CONFIG1H
 pragma config CPUDIV = OSC3_PLL3// CPU System Clock Postscaler (CPU system clock divide by 3 from 48MHz)
 pragma config CP0 = OFF        // Code Protect (Program memory is not code-protected)
 // CONFIG2L
 pragma config OSC = HSPLL      // Oscillator (HS+PLL, USB-HS+PLL)
 pragma config SOSCSEL = HIGH   // T1OSC/SOSC Power Selection Bits (High Power T1OSC/SOSC circuit selected)
 pragma config CLKOEC = OFF      // EC Clock Out Enable Bit  (CLKO output enabled on the RA6 pin)
 pragma config FCMEN = OFF      // Fail-Safe Clock Monitor (Disabled)
 pragma config IESO = OFF       // Internal External Oscillator Switch Over Mode (Disabled)
 define _XTAL_FREQ 16000000 //defined for delay
 char    ctxt[10], buf1, testcrc[4];    //buff serial string volatile unsigned int     ping, isrcall, index, reading, new_rx; int address, crc_high, crc_low, i, querylength; unsigned int result; //these variables are for fuctions to be enabled/disabled and pushed into application code via later int frame, z, bufferbits, query_reg_address, y; char data_out[64]; char reg_address[255];
 /*
  *
 Interrupt Service
 *
 UART RX and Timer 1
 *
 */
 void interrupt ISR() {
 if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
 {
     isrcall = 0x01;
     char temp;
     temp = RCREG;     // read the incoming data
     buf1 = temp;
     if(temp==address && reading==0)      //if my address..
     {
         index = 0;                  //reset index
         reading = 1;                //from now on go to else if
         LATCbits.LATC1 = 1;
         new_rx = 0;
         ping = 1;
     }
     else if(reading == 1)           //in middle of GPS sentence
     {
         //TODO reset timeout timer
         ctxt[index] = temp;         //load it up
         index++;                    //increment index
         ping = 1;                   //this is for debugging
         if(index > 6)              //1+7 = master frame.
             {
             index = 0;              //reset index
             reading = 0;            //no longer storing the string
             new_rx = 1;             //"ding"
             T1CONbits.TMR1ON = 0;
             }
     }
 }
 //RCSTA2bits.FERR = 0;    //Clear errors
 //RCSTA2bits.OERR = 0;
 //time out timer, if tripped new_rx=0;
 if (PIR1bits.TMR1IF)
 {
     new_rx=0;
     ping = 0;
     T1CONbits.TMR1ON = 0;
     PIR1bits.TMR1IF = 0;
     if (reading) {
     reading = 0;
     LATCbits.LATC0 = 1;
     }
 }
 } 
 void init_io(void) {
 INTCONbits.GIE = 0;         //no interruptions please ADCON0 = 0b00000000;        //don't need any ADC ADCON1 = 0b00000000;        //speed Vref=AVdd, VssRef=AVss LATA = 0x00; LATB = 0x00; LATC = 0x00; TRISAbits.TRISA0 = 1; // address 1 TRISAbits.TRISA1 = 1; // address 2 TRISAbits.TRISA2 = 1; // address 3 TRISAbits.TRISA3 = 0; // output TRISAbits.TRISA5 = 0; // output TRISBbits.TRISB0 = 0; // TRISBbits.TRISB1 = 1; // TRISBbits.TRISB2 = 0; // TRISBbits.TRISB3 = 0; // TRISBbits.TRISB4 = 0; //  TRISBbits.TRISB5 = 1; //  TRISBbits.TRISB6 = 0; //  TRISBbits.TRISB7 = 0; //  TRISCbits.TRISC0 = 0; // timer LED output TRISCbits.TRISC1 = 0; // output TRISCbits.TRISC2 = 0; // LED test output TRISCbits.TRISC3 = 0; // output //TRISCbits.TRISC4 = 1; // USB //TRISCbits.TRISC5 = 1; // USB TRISCbits.TRISC6 = 0; // output TX1 TRISCbits.TRISC7 = 1; // input  RX1
 }
 void __delay_10ms(unsigned char n)     //__delay functions built-in can't be used for much at this speed… so!
  {
      while (n-- != 0) {
          __delay_ms(10);
      }
  }
 void uart_xmit(unsigned int mydata_byte) {
 while(!TXSTAbits.TRMT);    // make sure buffer full bit is high before transmitting TXREG = mydata_byte;       // transmit data
 }
 void write_uart(const char txt) {                                 //this send a string to the TX buffer                                 //one character at a time        while(txt)
        uart_xmit(*txt++);
 }
 void serial_init(void)
 {
     //9600 8N1
 TXSTA1bits.SYNC = 0; TXSTA1bits.BRGH=1;       // select low speed Baud Rate (see baud rate calcs below) TXSTA1bits.TX9=0;        // select 8 data bits TXSTA1bits.TXEN = 1;     // enable transmit RCSTA1bits.SPEN=1;       // serial port is enabled RCSTA1bits.RX9=0;        // select 8 data bits RCSTA1bits.CREN=1;       // receive enabled SPBRG1=102; SPBRGH1=0; PIR1bits.RCIF=0;        // make sure receive interrupt flag is clear PIE1bits.RCIE=1;        // enable UART Receive interrupt INTCONbits.PEIE = 1;    // Enable peripheral interrupt INTCONbits.GIE = 1;     // enable global interrupt  __delay_10ms(5);        // give time for voltage levels on board to settle
 }
 void run_timer (void) {
     T1CONbits.TMR1ON = 0;
     TMR1=0;
     TMR1L=0x00;
     TMR1H=0xAA;
     T1CONbits.TMR1CS = 0x01;
     T1CONbits.T1CKPS = 0x01;
     PIE1bits.TMR1IE = 1;
     T1CONbits.TMR1ON = 1;
 //        PIR1bits.TMR1IF=0;
 }
 void check_my_address(void) {
     /*
      *  Determine what address the unit is based on DIP switches
      *  PORTAbits.RA0   Switch 1
      *  PORTAbits.RA1   Switch 2
     */
 if (PORTAbits.RA0 & PORTAbits.RA1) address = 0x04; if (!PORTAbits.RA0 & PORTAbits.RA1) address = 0x03; if (PORTAbits.RA0 & !PORTAbits.RA1) address = 0x02; if (!PORTAbits.RA0 & !PORTAbits.RA1) address = 0x01;
 }
 /*
 TODO
 Poll the inputs
 *
 I will probably use a 74138 to address banks with 74HC245 for byte/banks of inputs
 I will likely use the same set up for "Coils"
 For input registers (analog inputs) I will consider perhaps some kind of A/D input with CD4066+74HC138
 *
 */ 
 void poll_my_inputs(void) {
 //just stuff it with something for now for (z = 0; z < 64; ++z) { reg_address[z] = z; }
 }
 /*
 did the chicken come before the egg?
 this MODbus CRC bit was gently borrowed from the internet..
 I can't determine who wrote it.. it shows up in an Ardiuno Sketch for MODbus
 Modbus over serial line - RTU Slave Arduino Sketch
 with referenced of Juan Pablo Zometa, Samuel Marco, Andras Tucsni, Philip Costigan
 It also shows up on a number of websites and forums uncredited… many PIC or C based ..
 so who knows, but I didn't write it
 The MODbus CRC-16 function is outlined on the Modicon site for reference
 */
 unsigned int modbus_crc(unsigned char buf[], int start, int cnt)
 {
 int     i,j;
 unsigned temp, temp2, flag;
 temp=0xFFFF;
 for (i=start; i<cnt; i++){
    temp=temp ^ buf[i];
 for (j=1; j<=8; j++){       flag=temp & 0x0001;      temp=temp >> 1;
      if (flag) temp=temp ^ 0xA001;
      }
   }
 /*** Reverse byte order. ***/
 temp2=temp >> 8;
 temp= (temp << 8) | temp2;
 return(temp);
 } 
 int check_master_crc (void) {
 int diff, diff1, diff2;       //what we send back char data[6]; result = 0x0000; crc_high = 0x00; crc_low = 0x00; data[0] = address;  //this is ugly but all master queries are 6bytes+CRC so it'll do data[1] = ctxt[0];  //function data[2] = ctxt[1];  //start_addressH data[3] = ctxt[2];  //start_addressL data[4] = ctxt[3];  //# of regH data[5] = ctxt[4];  //# of regL result = modbus_crc(data,0,6); crc_high = result >> 8; crc_low = result & 0x00FF; diff1 = ctxt[5] ^ crc_high; diff2 = ctxt[6] ^ crc_low; diff = diff1 + diff2; //this spits back an XORed value between calculated and received CRC return(diff);
 }
 void respond_input_status(void) {
         //ctxt[1] start_addressH
         //ctxt[2] start_addressL
         //ctxt[3] # of regH
         //ctxt[4] # of regL
     querylength = ctxt[4];  //ignoring upper byte for now (TODO)
     query_reg_address = ctxt[2]; //ignoring upper address byte (TODO)
 frame = 0;     //data frame counter do {     data_out[frame] = reg_address[query_reg_address];     //TODO  make this do somethng     querylength--;     frame++; } while (querylength>0); querylength = ctxt[4];  //reload so we can count time transmitting data result = modbus_crc(data_out,0,2); crc_high = result >> 8; crc_low = result & 0x00FF; //uart_xmit response address, function, data_out[frames], CRC uart_xmit(address); uart_xmit(ctxt[0]); frame = 0; do {     uart_xmit(data_out[frame]);     //TODO  make this do somethng     querylength--;     frame++; } while (querylength>0); uart_xmit(crc_high); uart_xmit(crc_low);
 }
 int main(void) {
     OSCCONbits.SCS = 0x00;
 new_rx=0; init_io(); serial_init(); run_timer(); check_my_address(); y += address; write_uart("MODBus "); uart_xmit(y); LATCbits.LATC0 = 1; LATCbits.LATC1 = 1; __delay_10ms(50); LATCbits.LATC0 = 0; LATCbits.LATC1 = 0; if (address == 0x01){     LATCbits.LATC0 = 1; } if (address == 0x02) {     LATCbits.LATC1 = 1; } __delay_10ms(50); LATCbits.LATC0 = 0; LATCbits.LATC1 = 0; INTCONbits.GIE = 1; INTCONbits.PEIE = 1; while (1) {     //If read data, not me and then if no 232 traffic for >800us then any xmit over     //104 us (9600) x 8 quiet periods minus some for wiggle.     if (ping) {         run_timer();     }     if (new_rx) {       //this device was polled         //testing response         i = check_master_crc();     //check master query crc         if (i==0x00) {              //CRC Ok?             if (ctxt[0] == 2) {       //coil status query                 respond_input_status();             }             //TODO other types of functions go here         }         //all done, get ready for new query         new_rx = 0;     }     //testcrc[0] = 0xFF;     //testcrc[1] = 0x02;      //right now this is only thing processed     //result = modbus_crc(testcrc,0,2);     //crc_high = result >> 8;     //crc_low = result & 0x00FF;     __delay_10ms(10);     LATCbits.LATC2 = 1; //LED blinky test     __delay_10ms(10);     LATCbits.LATC0 = 0;     LATCbits.LATC1 = 0;     LATCbits.LATC2 = 0; }
 }

Why you shouldn’t open a makerspace.

Five years ago I had an itch to talk to other people; so overrated.

The End.

OK, fine.. I originally was thinking of some electronics club but after talking with a few people and refining my ideas through research, it was clear this idea fit best as a makerspace. I bought a meetup.com subscription. For as crappy as it is, Meetup does have a surprising network of members and access to kickstart your local community group. There is no question SnoCo Makerspace wouldn’t be if it wasn’t for Meetup. I set a meeting for a couple weeks out in a Lynnwood library and 35 people showed up. I gave these suckers my pitch… and I got back: “What’s your makerspace going to have in it?” … Of course I’d thought about what I wanted but I gave her the predictable answer along the lines of “OUR makerspace will have whatever interests the membership”. I had my own makerspace… at home, I was looking for something social.

That leads me to my first suggestion*: 1. The only successful reason for starting a makerspace should be social. If your core members don’t evolve fairly quickly into friends you’re not going to make it, prove me wrong.

We avoided a couple misteps in attempting, but failing, to start out at a local college (avoid at all costs), a port authority and later on another college. Outside of finding someone with really-free space, you’re going to have to figure out how to do it on your own. Don’t rely on any entity unless you have the check in your hand but it’s fairly rare to find free-money. If you don’t believe me pop onto the Nation of Makers facebook group and ask the question… NOM is kind of a lobbying group for makerspaces but they don’t really do anything outside run a convention. It had a purpose? There is a little history there if you’re bored and want to look it up.

Our first space; the only thing we found that worked. We found a small space in a space designed for micro-businesses. It was enough for 10 core members and visitors to use a laser cutter, 3D printer, and an electronics lab. This was a good bootstrap project and I ended up fronting most of the money for the lease outside a couple generous large checks when we started. Costs were offset by 10-15 members dues ($40, with the understanding we knew we weren’t work it but build it and they will come). Once we started workshops the space broke even.

Which brings me to tip #2: Don’t start a makerspace unless you already own many of the tools or know someone you can borrow them from. I’m talking core tools… they don’t have to be the best… just usable and safe, bonus points for aesthetically pleasing.

The adventure is a series of leaps of faith moments, scares, disappointments, and elation to success. You should have some business sense, avoid cliques but allow for them, be likable to most, don’t talk poorly of anyone, be positive and strive to inspire… above most take care of your toxic members, one or two can ruin your group and the experience of anyone coming through the door for a first peek. Make sure you have someone with a positive attitude greet and talk to every single person who comes through the door. Ask them questions to include what they’re interested in and what they expected.

SnoCo n Everett Grand Opening
Chas performing the ribbon cutting at SnoCo Makerspace new location

Our Makerspace 2.0 came out of space necessity. It would have been nice to be “in Seattle” but outside SoDo nothing had/has survived being in Seattle. It’s too expensive. We chose to move a few miles north of Lynnwood because space was cheaper, available, and Everett is a manufacturing town. Located 23 miles north of Seattle the makerspace pulls people in who live in North Seattle, Bellevue and north. The rent in Everett is affordable and for most of our existence outside the first few months of start-up and a “big leap” the space has been sustainable through membership and workshop income.

Okay.. so what sucked?

  1. Time. I spent more time on on this makerspace in the beginning than my 40-hour/week job. It tapered off a little here and there but finding that core group to help.
  2. Workshops. Finding people to teach courses… that can teach. I recommend creating a workshop template and guide… ask them to help you create hand-outs and documents for reoccurring workshops. It’ll help multiple people teach the same thing, the won’t forget important items like safety.. and there will be perceived value from attendees. Even when you pay people or credit dues… e-mails blasts will get the attention of a few people willing to teach: you should corner people and get a commitment with a date. If you don’t have workshops you probably will not succeed. People want to learn.. we have plenty of people who take workshops but never even use the tool… they just wanted to know how. Most of our attendees aren’t even members.
  3. Money. So much out of pocket cash.. particularly if you have to buy tools first. (Pro-tip #3. Tool-drives are nice…but be prepared to make a goodwill drop off when you’re done.. you get a lot of garbage with the occasional gems) .. I don’t know how many thousands of dollars I’ve spent over four years.. I don’t even want to calculate it roughly. Well in excess of $20k… probably a few times that. If you find big donors or business sponsors… well buy lotto tickets. No one gives away money… we have a couple companies that match but matching and donations make up less than 5% of our income.
  4. Space. What a pain… It takes forever to find a space.. then if it’s not close enough to usable you have to do some construction.. and decide if you’re going to do it legit or shady-maker-style. I tried for months working with city and county government entities to find low cost options… oddly Everett does give away very cheap rent in one of there buildings to some non-profit that is kind of a consignment shop for “makers”… mostly knitted stuff, books, jam, etc.. they didn’t have any interest in discussing possibilities with me but it’s good to have the contacts anyways. Doesn’t hurt to try, maybe you’ll get lucky.
  5. Insurance. Originally it took WEEKS of calling to find an insurance broker who could find a company to insure us. Over time we found another company, and then a company recommended an agent that did pick us up for a little more money but more flexibility. I never did find a company that would allow a minor to touch anything… I recommend selling your makerspace as very similar to some kind of industry or education center. Stick to selling yourself as a CBO to donors and such. You’ll find issues finding insurance companies that will let you play with real toys if you are a “club”, but don’t lie.. or you might as well not even have insurance.

Tip #4. Don’t start a makerspace if you think you’re going to make an income at it. Eventually you’re hire someone which is as big step, but no one on your board is going to want to pay that person what they’re worth when they’ve been donating all that time. Your first hire will likely be half-book keeper because that seems to be about 1/2 of the reoccurring work once you’re stable and of first-hire size.

What didn’t suck?

I don’t regret starting the makerspace, I wouldn’t do it again and I am pretty sure I wouldn’t have done it if I knew what I know now.. but I am glad I did and it was worth it. I’ve done plenty of volunteer work before, if I hadn’t I suspect I’d feel more fulfilled in that regard. I created (with A LOT of help) a wonderful organization that is inclusive and provides a ton of social interaction, mentoring, and support to our members, guests, and community (we do some out reach… fix 3d printers for schools, etc.). There are a couple really enriching moments over the last few years… the best part is many of my now-closest friends I met in the makerspace… so that itch to talk to people was thankfully fulfilled.

My Cred: I founded the largest** makerspace in Washington State; I’ve helped consult start-up three other spaces, inspired others and seen dozens fail, most of them before they really got started. **We have a commercial fab lab which is larger but it’s really a commercial fab business, they have more sq ft.. Largest as measured by membership and presumed participation.

*In no way should this reflect any kind of government ran/funded makerspace to include schools, city government, museum spaces, art council funded space, etc. Additionally I do not consider incubators or co-working spaces to be makerspaces.