Je t’embrasse Salutations from Silicon Valley, California

3May/170

Python split() in C/C++

I don't know about you, but I rarely want to compress away multiple delimiters in my token-parsing... yet, if you look at the man-page for strtok(), thats exactly what it does:

A sequence of two or more contiguous delimiter bytes in the parsed string is considered to be a single delimiter. Delimiter bytes at the start or end of the string are ignored. Put another way: the tokens returned by strtok() are always nonempty strings.

Seems kinda silly, but that's just how it's been for years and years. So to get around it, I use the following (See accepted answer with regard to Python split() function)

char *strtoks(char *str, const char *delim)
{
    static char *tok = NULL;
    static char *next = NULL;
    char *m = NULL;

    if (delim == NULL) return NULL;

    tok = (str) ? str : next;
    if (tok == NULL) return NULL;

    m = strstr(tok, delim);
    if (m) {
        next = m + strlen(delim);
        *m = '\0';
    } else {
        next = NULL;
    }

    return tok;
}
Filed under: C/C++ No Comments
5Nov/150

The Human Glowstick – Part 2

Even though I began this project in August, attempting to adapt a OctoWS2811 implementation to be wireless; controlled by my phone...

What I'm actually going to walk through here is the end result. Which is simply a Adafruit WS2801 implementation on top of the Adafruit nRF8001 "callbackEcho" demo sketch.

PLEASE NOTE: At the time of writing this, the Adafruit WS2801 library does not include the Color() method... and as such, I branched it, created the method, and have made my modified version available on GitHub: Here (in all likelihood the change will have been merged into their mainline by the time you are reading this)

To give you an accurate idea on the scope of the software, remember that there are 4 parts to this project:

  1. The LEDs (238x WS2801 modules)
  2. The Bluetooth LE Module
  3. The Fan control (MOSFET)
  4. The Sound Detector

Whenever I start a project like this, I always try to make sure I have the basic understanding of any one component correct before moving on. (That's why each of those links above refer to a test that specifically targets one thing at a time).

One interesting tidbit that I continually forget: If you want to run your project without a console attached, you should probably not wait for a console to be attached... Basically, many Arduino example sketches run a setup() something like the following:

void setup(void)
{
    Serial.begin(9600);
    while(!Serial);
    Serial.println("Setup complete");
}

Only trouble is that when I take this setup into a costume, it will STILL require me to connect a serial console to it before I can continue past that while loop. It can be incredibly helpful to know that your sketch wont start till you connect the console to it... but on the other hand, you can spend an awful lot of time debugging why your sketch is not running when you "hide it away in your project"... but then mysteriously runs when you "look at it"...

Anyway...

Adafruit Color Picker iOS App

As the bare-minimum "glowstick" costume, you have to be able to glow... and given that the previous tests 1 & 2 above both passed... I decided to create the bare-minimum program using the Adafruit Controller ColorPicker.

What this allowed me to do was create another simple test program:

The nRF8001 + WS2801 Color Picker Test

Basically the only educational part of this demo is the breakup of "color picking" to "color setting" (the first being the action done on the App, and the second being the action done by the Teensy 3.2 to the WS2801 LED modules)

The main idea is to never block the main loop. An example of how I do this is to make sure that, instead of using delay functions, I instead just return to the main loop until a certain amount of time has passed.

Another example is inside the mechanism of color-picking. Every loop() starts with reading inputs (like the color #FF0000) and setting modes (in this case seeing a "!C" prefix in the BLE buffer immediately sets the mode to "color picker"). Every loop() ends with implementation of a single mode.

2Nov/150

The Human Glowstick – Part 1

As I know there was a significant interest in my costume this year, I am going to try and put together all of the directions on how I both constructed the foam exterior to the costume, and hooked up the 338 LED modules + Teensy + Bluetooth LE + SPL meter electronics to make the costume what it was.

As a first post, I will provide you with my electronics shopping list (that made the final production), getting into the foam in a later post.

Rainbow Mode

Rainbow Mode

Shopping list:

  1. Two 12V/20A DC regulators for powering the LEDs
  2. One 5V/3A DC regulator for powering the electronics
  3. Two Turnigy 4S 5000mAh 25C LiPo Batteries
  4. Two HXT LiPoly Battery Monitors for 4S packs
  5. Two 4mm HXT to 6x3.5mm Bullet breakout cables
  6. Ten 3.5mm Bullet Extension packs (pack of 3 wires)
  7. Eight pairs of 4-pin JST SM Plug/Receptacle Cable Sets
  8. One pair of 2-pin JST SM Plug/Receptacle Cable Sets
  9. Ten feet of 12AWG Silicone Wire
  10. Two 4mm HXT 10cm Silicone Wire extensions (battery side) - would actually recommend NOT using the same for battery/ESC...
  11. Two 4mm HXT 10cm Silicone Wire extensions (ESC side) - would actually recommend NOT using the same for battery/ESC...
  12. 400 (only used 238) WS2801 12v 4xLED modules
  13. One MOSFET power Control kit
  14. One SparkFun Sound Detector
  15. One Bluetooth LE (nRF8001) module
  16. One Teensy 3.2 (32-bit ARM processor) Microcontroller
  17. One 74HCT245 bus transceiver chip

 

Additional but unknown quantities:

  1. Rosin-core solder
  2. Solder-wick
  3. Solderless breadboards
  4. Jumper Kit
  5. Heat shrink tubing
  6. Zip ties (6 inch & 12 inch)
  7. Resistors of various values (for tuning mic sensitivity)

I will try to go step-by-step with the directions as soon as I can, but I hope this starts you off with a "big picture" of how much time/effort/money is involved in such a project... really that is it though, cause most of the difficult things have been taken care of by using "off the shelf" modules from people like SparkFun & Adafruit. 🙂

15Aug/150

Teensy 3.1 SdFat File Browser

I was working on making a test application for the SDFat library, but I eventually ended up creating was more of a Linux-style file browser.
All that I currently have implemented is "cd", "ls", and a SdFat-style "ls -l"...

/*
 * Print size, modify date/time, and name for all files in root.
 */
#include <SPI.h>
#include <SdFat.h>

#define LINUX_PROMPT_STYLE

// SD chip select pin
const uint8_t chipSelect = 4;

// file system objects
SdFat sd;
SdFile file;
SdBaseFile *cwd;
String path;

// =============================================================================

void printPrompt()
{
  
#ifdef LINUX_PROMPT_STYLE
  Serial.print("[root@localhost \"");
  Serial.print(path);
  Serial.println("\"]$ ");
#else
  Serial.println("#");
#endif

}

void cmd_ll(SdBaseFile *d)
{ 
  // cache the current d position
  FatPos_t p;
  d->getpos(&p);
  
  // open next file in root.  The volume working directory, vwd, is root
  while (file.openNext(d, O_READ)) {
    file.printFileSize(&Serial);
    Serial.write(' ');
    file.printModifyDateTime(&Serial);
    Serial.write(' ');
    file.printName(&Serial);
    if (file.isDir()) {
      // Indicate a directory.
      Serial.write('/');
    }
    Serial.println();
    file.close();
  }

  d->setpos(&p);
  printPrompt();
}

void cmd_ls(SdBaseFile *d)
{
  // cache the current d position
  FatPos_t p;
  d->getpos(&p);
  
  // open next file in root.  The volume working directory, vwd, is root
  while (file.openNext(d, O_READ)) {
    if (file.isHidden()) {
      file.close();
      continue;
    }
    file.printName(&Serial);
    if (file.isDir()) {
      // Indicate a directory.
      Serial.write('/');
    }
    Serial.println();
    file.close();
  }
  
  d->setpos(&p);
  printPrompt();
}

void cmd_cd(SdBaseFile *d, String *args)
{
  if (*args == "") {
    if (sd.chdir()) path = "/";
  }
  else if (*args == ".") {
  }
  else if (*args == "..") {
    int slash = path.lastIndexOf("/", path.length() - 1);
    String temp;
    if (slash == 0) temp = "/";
    else temp = path.substring(0, slash);
    
    char pbuf[512];
    memset(&pbuf, 0, sizeof(pbuf));
    temp.toCharArray(pbuf, sizeof(pbuf));
    if (sd.chdir(pbuf))
      path = temp;
  }
  else if ((*args)[0] == '/') {
    int slash = path.lastIndexOf("/", path.length() - 1);
    String temp = path.substring(0, slash);

    char buf[64];
    memset(&buf, 0, sizeof(buf));
    args->toCharArray(buf, sizeof(buf));
    if (sd.chdir(buf))
      path = *args;
  }
  else {
    int slash = path.lastIndexOf("/", path.length() - 1);
    String temp = path.substring(0, slash);

    char buf[64];
    memset(&buf, 0, sizeof(buf));
    args->toCharArray(buf, sizeof(buf));
    if (sd.chdir(buf)) {
      if (path == "/") {
        path.concat(buf);
      } 
      else {
        path.concat("/");
        path.concat(buf);
      }
    }
  }

  printPrompt();
  return;
}

// =============================================================================

#define CMD_IS(x,y) (x.substring(0, strlen(y)) == y)
//#define SAVE_ARGS(x,y) { Serial.println(x); Serial.println(y); };
#define SAVE_ARGS(x, y) { args = x.substring(strlen(y) + 1); }
int getInputCommand()
{
  String args;
  
  if (!Serial.available()) return -1;  
  String i = Serial.readString();

  // Flush
  while (Serial.available()) { Serial.read(); Serial.print("."); };
  
  if CMD_IS(i, "ls -l") {
    cmd_ll(cwd);
    return 0;
  }
  else if CMD_IS(i, "ll") {
    cmd_ll(cwd);
    return 0;
  }
  else if CMD_IS(i, "ls") {
    cmd_ls(cwd);
    return 0;
  }
  else if CMD_IS(i, "cd") {
    SAVE_ARGS(i, "cd");
    cmd_cd(cwd, &args);
    return 0;
  }

  return -1;
}

//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while (!Serial) {} // wait for Leonardo
  delay(400);

  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }

  cwd = (SdBaseFile *)sd.vwd();
  path = "/";
}

//------------------------------------------------------------------------------
void loop()
{
  getInputCommand();
}
31Mar/150

Homemade Bubble Tea

The other night my girlfriend and I had the inspiration to create homemade bubble tea. In an effort to reproduce my favorite Roasted Barley Milk Tea, we stumbled upon several tips and tricks, and even a pretty decent Regular Milk Tea recipe. Although we chose to make each piece of this recipe individually, I realized afterwards that much (if not all) of these recipes can be done in parallel, greatly decreasing the total time spent producing a cup of Boba.

Base Milk Tea Ingredients:

Tapioca Ingredients:

Now, if you buy everything exactly as it is on the above list, you will notice that the following directions are really just taken off each individual package. But of course, for your benefit, I have combined these (and a few of my own) directions here for you.

Roasted Barley Milk Tea Directions:
Starting with your 1L of water in a saucepan, and 2T of roasted barley (preferably inside a tea-infuser, so that you don't have to filter it later), bring the barley to a boil, and continue to just barely keep the boil going over medium-high heat for 5 minutes. Remove the tea-infuser or filter the barley from the water, and add the 9 Ceylon Black Tea bags to the barley-water. Reduce heat to minimum, and allow tea bags to sit for 3-5 minutes. Squeeze and remove tea-bags. Whisk in a heaping 1/4 C of non-dairy creamer, and 1/4 C of sugar (additional sugar to taste). Set aside for 2 hours, or cool over an ice bath.

To cool via an ice bath, prepare a large bowl with ice that can fit the bottom of your saucepan in it. Continually stir the tea as the ice gradually cools the pan to a more manageable temperature. Boiling-temperature to ice-cold should be possible with about 10 minutes of work.

Sugar-Water Directions:
To make a sugar-water mixture (to keep the pearls in once they are cooked), simply bring to a boil 1 cup of water with 1 cup of sugar. Stir continuously, and once boiling, remove from heat and allow to cool completely. It will be runny, and the consistency of a thin syrup.

QQ Pearl Directions:
Prepare a medium bowl with about 4 cups of cool water (not iced cold) in it. In a large pot (you need a lid) bring about 20 cups of water to a rolling boil. Add the 2 cups of tapioca pearls to the water, and wait for them to rise to the top of the water. Once floating, cover the pot with the lid, and continue to boil for 8 minutes. Turn off heat, and leave for another 8 minutes. Strain pearls from hot water and place in cool-water bowl that you prepared earlier. Let stand for 1 minute. Strain pearls from cool-water, and place in sugar-water that was prepared earlier.

Filed under: Projects, Recipes No Comments
6Nov/130

Local rpmbuild directory

Just so I don't forget this for the 26th time...

Typically, when you install a source RPM, it unpacks the source code in some location like "/usr/src/redhat". Unfortunately if you are not root, and do not have sudo permissions, you just don't have access to that directory. So instead, it would be helpful to have the ability to do any RPM building in a user-local directory... like "~/rpmbuild".

To do this, all you have to do is make the directory:

[jjinno@localhost ~]$ mkdir -p ~/rpmbuild

And then create yourself a file called "~/.rpmmacros" with the following contents:

%_topdir %(echo $HOME)/rpmbuild
.%_smp_mflags -j3

And that's it!

Now when you run and RPM install of some source RPM, it will install the source code to "~/rpmbuild/SOURCES" and there will be an rpmbuild SPEC file located in "~/rpmbuild/SPECS". Simply change directory over to the SPEC file, and run your rpmbuild. (NOTE: just cause installing the source doesn't require root permissions does not necessarily mean that compiling the source will work without them)

Filed under: Linux No Comments
21Oct/130

Fedora 19 VNC setup

It has been a few releases of Fedora since I have installed a VNC server, and it appears that there is a great deal of confusion on the internet about how to do this... so I'm writing up what worked for me, so that some other poor soul can get this working. The end goal is to have a VNC server that: runs on Fedora 19, persists across reboots, and allows access to the XFCE4 Desktop Environment.

NOTE: I don't have Gnome/KDE even installed. This presents a challenge (not really) because the majority of the Fedora scripts are written to detect and work with Gnome/KDE and nothing else. In fact, if you dont have Gnome/KDE, you will usually end up failing back to TWM (yikes!).

First, we need to install the tigervnc-server:

[root@localhost ~]# yum install tigervnc-server
Loaded plugins: langpacks, refresh-packagekit
Resolving Dependencies
--> Running transaction check
---> Package tigervnc-server.x86_64 0:1.3.0-7.fc19 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch            Version             Repository        Size
================================================================================
Installing:
 tigervnc-server     x86_64          1.3.0-7.fc19        updates          196 k

Transaction Summary
================================================================================
Install  1 Package

Total download size: 196 k
Installed size: 481 k
Is this ok [y/d/N]: y
Downloading packages:
tigervnc-server-1.3.0-7.fc19.x86_64.rpm                       | 196 kB  00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : tigervnc-server-1.3.0-7.fc19.x86_64                           1/1
  Verifying  : tigervnc-server-1.3.0-7.fc19.x86_64                           1/1

Installed:
  tigervnc-server.x86_64 0:1.3.0-7.fc19

Complete!

For the moment we are going to configure the VNC service to be running on a sub-port of the VNC service port (port 5900). In this example, we will be creating port-3 (offset from 5900). This means that port 5903 will need to be opened, but when we connect to the system from a remote host using the VNC protocol, we will only have to specify something like "10.16.2.22:3".

To create a configuration for port-offset 3, lets perform the following copy:

[root@localhost ~]# cp /lib/systemd/system/vncserver@.service \
/etc/systemd/system/vncserver@:3.service

Once you have a port-specific configuration file, open the file in your favorite editor, and you will see lines like the following:

ExecStart=/sbin/runuser -l <USER> -c "/usr/bin/vncserver %i"
PIDFile=/home/<USER>/.vnc/%H%i.pid

Replace the "<USER>" tags with the appropriate user. In the case of "root" (which is what I used) you have to make sure you change the PIDFile path to be inside "/root/.vnc". In the end, my configuration file looked like this:

[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target

[Service]
Type=forking
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'
ExecStart=/sbin/runuser -l root -c "/usr/bin/vncserver %i"
PIDFile=/root/.vnc/%H%i.pid
ExecStop=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'

[Install]
WantedBy=multi-user.target

After configuring, we need to make sure the Fedora 19 firewall is prepared to allow VNC traffic in. To do this as a one-off for run-time only, you could use the following command:

[root@localhost ~]# firewall-cmd --zone=public --add-service vnc-server

However, if you want to have the firewall setting persist across reboots, you need to make that change permanent. To do so, just add the permanent option, like so:

[root@localhost ~]# firewall-cmd --permanent --zone=public --add-service vnc-server

If we were to start up our VNC server now, we would be able to log in (as I insinuated earlier), but would find ourselves dropped into a TWM desktop environment. I have nothing against TWM, but since the goal of this guide was to drop us into an XFCE4 desktop environment, TWM will simply not do.

Based upon soo many things out there on the interwebs, you may be tempted to edit some scripts, but I'm here to tell you that there is an easier way. The "/etc/sysconfig/desktop" file!

If your Fedora 19 system is like mine, then that file does not exist, and you need to create it. If that file does exist on your system, then there is one check you need to do: make sure "DESKTOP" is not specified in the file. I do that via a simple in-place sed-deletion:

[root@localhost ~]# sed -i '/DESKTOP/d' /etc/sysconfig/desktop

Then once we have eliminated any possibility that another desktop variable might be defined, we need to define another variable in this file. What you will see below is simply a way of defining the variable "PREFERRED" to be the full-path to the "startxfce4" program. Feel free to hard-code it instead of the lookup that I am doing:

[root@localhost ~]# echo 'PREFERRED="$(type -p startxfce4)"' \
>> /etc/sysconfig/desktop

Finally, we should setup a password for the VNC user to log in with. To do so, we first need to switch to that user, and then run "vncpasswd", like so:

[root@localhost ~]# su root
[root@localhost ~]# vncpasswd
Password:
Verify:

That is 100% of the configuration necessary to start your VNC server. Now all we need to do is actually start it. (and since we will probably reboot at some point, lets make the service persistent too)

systemctl enable vncserver@:3.service
systemctl start vncserver@:3.service

At this point, (assuming your network is working) you should be able to access this VNC server from another host. To do so, I simply open VNCViewer on my Windows box, specified "10.16.2.22:3", typed username/password... and VOILA!

I really hope this makes life easier for somebody. (Most likely me, the next time I try to remember what I did)

Filed under: Linux No Comments
9May/130

Electric Run SF

So here is the thing, we wanted a discount on registration for the Electric Run in SF... so we needed to get a group of people together: Done. Then we needed a team name... so I suggested we be "The Party Rock Crew" and dress appropriately... of course, being the crazy guy that I am, that meant me as Boxhead...

So I start down this crazy road of buying 400 (plus spares) super-bright LED modules from China, a crazy ton of batteries, and EL-wire, and then... I broke my foot. So Im done right? I sure thought so... but then 3 days before the event, these friends of mine suggested I rent a wheelchair, and they will make sure I get through the whole 5k... of course with 3 days, I had an entire day to procrastinate before starting this:

Boxhead with a mohawk!

Boxhead with a mohawk!

Pretty epic, right? So here was my list of ACTUALLY USED components in this project:

- 15x15x20 cardboard box
- 1x LED strip from China
- 2x (3m) EL wire strands
- EL Sequencer from SparkFun
- Tape + Gold Spray-paint
- Used/Returned bicycle helmet
- Hot Glue
- Black mesh/grid
- 2x 80mm Blue LED Computer Fans

If you look carefully, in the background you can also see the wheelchair which I added another 4 EL wire strands to and had them all running off the same EL Sequencer from SparkFun. (It's programmable, but comes with a default setting... which is awesome, since I had NO TIME!)... Also, believe it or not, but the LiPoly batteries I had wanted to power this with were unavailable... so I threw together some 8-packs of Duracel C batteries. Woot!

OK so, it took 3 of us, working till about 2am to get the mohawk made... and then painted, and then we glued & taped it to the top of the head on the day of. My roommate (seeing how frantic I was working) offered to program the LED modules on the mohawk (yep, thats right, the "tips of the mohawk" were 20 programmable LED modules).

End result? We got there later than we intended to, but WAY earlier than we needed to. So altogether perfect timing. It was of course freezing, and I had to keep begging my friends to let me push myself with my arms in order to stay non-hypothermic. LOL... in the end, I got up off my rump for a photo-op:

"Redfoo" forgot his afro in the car... :)

"Redfoo" forgot his afro in the car... 🙂

Thanks to everybody who made this possible. You guys rock!

Filed under: Costumes No Comments
5Apr/130

The “Blinky” Animation

So I finally got around to opening up a box of goodies the other week, including a bunch of 8x8 bi-color (red & green) LED matrices from SureElectronics. I have had these things for about 5 years now, always imagining that I would build some cool scrolling marquis or something... only problem is I never got around to using them at all.

LED Matrix (bottom) LED Matrix (top)

As you can see from the pictures, the LED matrix has 24 pins. This accounts for 8 common cathodes, 8 red anodes, and 8 green anodes. The only thing that you probably cant grasp from those two pictures is the fact that there is no diffusing material between the LEDs and your view. This means that when you try to color-mix some combination of red and green, you dont really see orange. Instead you see a red dot and a green dot, and you go blind because they are really bright... why did you look directly into them? who knows.

Micrel MIC2981 NXP 8-bit shift register

The two chips you see there are the Micrel MIC2981 current/voltage source driver, and the NXP 8-bit shift-n-store register. The former is pretty straight-forward, providing an isolated source of current to drive the LEDs such that the current is not being drawn from the data-pin of whatever you attach it to (in my case an Arduino Uno R3).

A SparkFun Plug The full project

The end result? Well it is hard to see here, especially since it is just a picture, and not an animation... but I have animated Blinky to wobble in from the right, wait for 5 seconds, and then wobble out the left side of the screen. This makes for a pretty decent ~10 second clip.

A look at Blinky

Filed under: Arduino No Comments
31Jan/130

Where the Wild Things Are

Carol

Meet Carol:

OK, so it has been about 3 years since I started this costume, and even though it is not NEARLY complete, it clearly belongs on my blog, filed under costumes.

The head is formed by riveting flexible PVC (with metal washers) to a jumbo-sized Halloween "Witches Cauldron". The cauldron shape (ie. the flat bottom of the cauldron) can still be seen if you take a look at the top of the head. This general shape was then filled out using industrial shipping foam, and covered in plastic-wrap, glue, and finally stretched felt.

I wish I had a closeup of the claw-gloves, cause they are pretty cool... Essentially I took the cheapest pair of garden gloves I could find, and glued a fur hand-cutout to the top of the fingers. Then I took the teeth from a caveman necklace and used them as the claws. Finally, I affixed pieces of pink-ish foam on the palms to create paw-pad look.

I had planned to make some feet based on a tutorial I had seen for making some wolf-feet... but I ran out of time before Halloween! DOH!

Filed under: Costumes No Comments