Thursday, June 20, 2013

Ansible overview - implementing ntp.


Ansible == control all now to go to the pub sooner.  

Or break it all in an instant and cry lots.  It's all up to you :)


s/chef/ansible/g

Why Ansible and not something else?

Automating sysadmin work is nothing new, we've all been doing that for ages.  Automation tools are nothing new either, and there are plenty around to choose from.  After a failed attempt at puppet i tried ansible after hearing about it at #lca2013.  I liked ansible because it felt as comfortable to use as ssh and vi, but had the power of idempotence, parallel execution, platform independence, operating system agnosticism, it was simple for me to understand and so on.  It might not work for you :) but if you do try it, please let me know how you get on.

So, ansible is a tool which lets you perform actions on servers.  They can be single actions, or multiple actions based on a definition called a playbook. You write a playbook (don’t worry, it’s not hard) where you define what needs to be done and you execute it with ansible. There’s no server-side software needed (well, except for python) and there’s hardly any configuration. By managing your servers with ansible you not only make sure their configuration is always identical and you always have the right tools installed, and you also have your server configuration documented at all times.

How to install ansible

Ansible is a python app, and it can be installed using the python software manager, "pip"; have a look at http://www.ansibleworks.com/docs/gettingstarted.html.

Architecture and authentication

Ansible works by connecting to your nodes via SSH (or locally) and pushing out small programs, called “Ansible Modules” to them. These programs are written to be resource models of the desired state of the system. Ansible then executes these modules over SSH, and deletes them when finished. Your library of modules can reside on any machine, and there are no servers, daemons, or databases required. Typically you’ll work with your favorite terminal program, a text editor, and probably a version control system to keep track of changes to your content.

Bootstrapping a host for ansible.

As mentioned earlier, ansible isn't a client/server model, in that it has no server daemon nor clients.  It just runs over ssh from your machine to the remote host.  The remote host sometimes need remote hosts may need python-simplejson installed.  They also need to allow ssh via ssh certificates, such as via:

$ ssh-copy-id remotehost
$ ssh-copy-id root@remotehost
$ ssh root@remotehost apt-get install python-simplejson

You can also have ansible use sudo instead of root@ however that option is not explored in this doco.  And Yes, somebody worked out how you can bootstrap a host for ansible, using ansible ..

Hosts definition

For ansible to work, you need a hosts definition. The default host definition is located in /etc/ansible/hosts. You can use a custom hosts definition (located outside /etc, for example) by defining them elsewhere and passing the -i [host-file location] parameter to the ansible-playbook command when running a playbook. We’re just going to work with /etc/ansible/hosts for now.

Now let’s open up the empty hosts file and put the following contents in it:
[kvmhosts]
thylacine
feathertail
bettong
kaluta
#lycosa

What we’ve done here, is define a group named ‘kvmhosts’. A group can be used in a playbook to run the playbook against a number of hosts; you can't run ansible against a host if it's not mentioned in this file.  We’ve added some hosts to that group by their hostname, but you can use IP addresses as well; either way they need to be reachable and bootstrapped as discussed earlier. You can also define just one host here and you don’t even need a group. Multiple groups are OK too, of course.  Even groups of groups.  It’s all up to you and it all happens in this file.

Task execution with ./ansible

So lets say we want to make sure all our kvmhosts have the correct time.  How can we check the time on all the hosts at once? we can run ansible with the command module, call date and see what is returned;

$ ansible kvmhosts -a 'date'
bettong | success | rc=0 >>
Mon Jun 17 11:42:34 EST 2013

kvmtest | success | rc=0 >>
Mon Jun 17 10:46:02 EST 2013

feathertail | success | rc=0 >>
Mon Jun 17 11:46:02 EST 2013

thylacine | success | rc=0 >>
Mon Jun 17 10:44:02 EST 2013

kaluta | success | rc=0 >>
Mon Jun 17 11:46:11 EST 2013

Notice we didn't actually say "run the command module"?  if we don't specify a module name explicitly, then ansible assumes that's the one you want; but there are dozens of others.  If you don't specify a user to connect as, then ansible will assume you mean to connect as yourself.

Ansible modules

So there is the command module (like we used above) which just allows us to run 'date' against a group, but what if we want to do something more interesting?  enter ansible modules.  Don't worry though, you don't need to know how they work internally, just that they just do, and there's lots of them at http://www.ansibleworks.com/docs/modules.Sohtml.

How about we want to make sure the httpd is running on all our webservers?  easy;
$ ansible webservers -u root -m service -a "name=httpd state=running"

Or that htop is installed on all your debian machines;
$ ansible debianservers -u root -m apt -a "pkg:htop state=present"

Or create a symlink;
$ ansible debianservers -u root -m src=/file/to/link/to dest=/path/to/symlink owner=foo group=foo state=link

You can do any *single task* you like in this way.  There is no program control or audit or any real repeatability in this mode, but sometimes this is all you might need.

Writing your first playbook

Obviously however, doing anything significant takes more than one task   In a playbook you define what "task"ansible should take to get the server in the state you want. Only what you define gets done.  In ansible, all tasks are idempotent which means that they only change what need to be changed and otherwise do nothing.  So you can run a playbook against a host repeatedly, and after the first iteration, nothing will be changed.  Or add a host to a group, run the playbook against the group, and only the new host gets configured etc.

Playbooks are executed with ./ansible-playbook.  Playbooks are written in YAML which means they are fussy about indenting and syntax and their special markers.  it's a bit annoying.

Making NTP work 

Lets imagine the time on all our kvm hosts is wrong.  We want to use ansible to make sure they all have NTP installed and that it's configured sanely and running etc.  Doing this manually would take a good number of steps which has to be repeated on each host; when we have multiple tasks like this, that's when we write an ansible playbook. When we get a new host, or change the config somehow, we just run it again and voila! it's done.

So lets imagine that we need to complete the following in order to get NTP working on a debian host;

  1. Do an apt-get update.
  2. Install the ntp package
  3. Unless this is called timelord, configure NTP to point to timelord
  4. Configure the timezone files appropriately.
  5. Restart NTP
  6. Wait awhile and check to see it's working OK.

For any given host, if any of these steps fail then we'd want to stop configuring it to see any error message(s) etc.

ntp.yml

An ansible playbook to complete those steps looks largely like a script written in plain english.

If you have a look below, you can see that this playbook is able to be run against all hosts, and will connect to each of them as the root user.

##
# Demo Ansible playbook for installing and configuring NTP
#
---
- hosts: all
  name: install, configure and start NTP
  user: root
  tasks:
    # Ensure the ntp package is installed
    - name: Ensure the ntp package is installed      
      action: apt update_cache=yes pkg=ntp state=installed

    # Our local time server is called timelord.  all servers need to point to that.
    - name: EXCLUDING timelord, copy over our local ntp.conf
      when_string: $ansible_hostname != 'timelord' 
      action: copy src=files/etc-ntp.conf dest=/etc/ntp.conf mode=755
    # Update /etc/localtime to point to files/usr-share-zoneinfo-Australia-Hobart
    - name: Update timezone link foo
      action: copy src=files/usr-share-zoneinfo-Australia-Hobart dest=/etc/localtime mode=755
    # Update /etc/timezone to show Australia/Hobart
    - name: Update timezone link foo
      action: copy src=files/etc-timezone-hobart dest=/etc/timezone mode=755
    # Restart ntp to apply new ntp.conf
    - name: Restart ntp to apply new ntp.conf
      action: service name=ntp state=restarted

And it is executed simply running;
$ ansible-playbook ntp.yml --limit kvmhosts

If a task fails on a given host, then execution of the remaining tasks on that host would be stopped.  You would be able to see what happened and fix it etc.

And finally, check your work and make sure NTP is indeed up and running by using ansible in task execution mode like we did earlier;

$ ansible kvmhosts -m command -a 'date'
bettong | success | rc=0 >>
Mon Jun 17 14:22:20 EST 2013

kaluta | success | rc=0 >>
Mon Jun 17 14:22:20 EST 2013

kvmtest | success | rc=0 >>
Mon Jun 17 14:22:20 EST 2013

thylacine | success | rc=0 >>
Mon Jun 17 14:22:20 EST 2013

feathertail | success | rc=0 >>
Mon Jun 17 14:22:20 EST 2013

Facts and logic and all the rest of it

This document shows the basics of ansible, and doesn't explore some of the more interesting aspects, such as conditional processing (ie when_string: $ansible_hostname != 'timelord'), or how to manage playbook execution between different host groups (hosts: all:!depricatedmachines) and so on.

There is alot of documentation on the ansible website, so if you're tempted, then please do have a look.

Internet resources and thanks to;


Tuesday, April 16, 2013

ABC Dig Radio now playing list

Dig. The link between the music i love, and the wide wide world of amazing.

They play awesome songs and i love it, and part of that awesome is the total absence of DJ's rabbiting on all day about .  A side effect however is that there is nobody to mention who just played that awesome song you just heard.  Enter the ABC dig music now playing list.  There are lots of ways to get it;
  1. Straight off their homepage ;) http://abcdigmusic.net.au/
  2. From their mobile app
  3. From the @ABCDigMusicNow twitter feed
  4. Or from any number of tools which parse http://abcdigmusic.net.au/player-data.php
But I couldn't find a command line tool to do the job, so i wrote one in python.  It's pretty basic, and looks like this;
$ ./abc-dig-music.py 
Now: 'When A Woman Loves A Man' by Paul Kelly from 'Spring And Fall' (2012) on Univeral Music
Was: 'You Come Through' by PJ Harvey from 'Uh Huh Her' (2004) on Island/UMA
But I can't claim much of it is my own work, I'm just capitalizing on the good work already done by Ian Wienand in his Dig Jazz Applet.  But like all awesome people, he GPLd his code :)  thanks mate.

Thursday, March 07, 2013

Return codes from system commands in Perl

So say I'm running a Perl script which then calls some system command, let's say 'echo blah'.  Running that returns three things to us;
  1. the actual output (stdout) which would be "blah",
  2. any error output (stderr) which would be "" ie; NULL or empty,
  3. and the return code which would be "0";
So until now I had to choose which one of those i care about most and use the appropriate system executable handler to suit; let me explain;

my $result = `echo blah`; # captures "blah" into $result
my $result = system("echo blah"); # captures the return value of echo into $result
..and i stopped paying attention about there.

Suffice to say, no method i knew about allowed me to capture them all at the same time. Until i discovered IO::CaptureOutput.  Check out this foo;
$ cat ./perl.system.calls.sh 
#!/usr/bin/perl
use strict;
use IO::CaptureOutput qw/capture_exec/;

my @cmd= "echo blah";

my ($stdout, $stderr, $success, $exit_code) = capture_exec( @cmd ); chomp($stdout);

print "running '@cmd'\n";
print "stdout = $stdout\n";
print "stderr = $stderr\n";
print "success = $success\n";
print "exit_code = $exit_code\n";

$ ./perl.system.calls.sh 
running 'echo blah'
stdout = blah
stderr = 
success = 1
exit_code = 0

So now i can trap these in my code and handle them each as i see fit.  It means better error handling and software that runs (and stops!) how and when one might want it to.  And that's cool.

Wednesday, March 06, 2013

Migrating VMs on KVM without shared storage

So you have some virtual machine hosts running the Linux virtualisation software KVM on a current release of Ubuntu.  One of the hosts needs some urgent maintenance requiring an outage soon, but that host has several business critical virtual machines on it.  So you need to migrate those vms to the other host.

That's cool though because virsh allows us to migrate virtual machines to different hosts, assuming they are sharing the same storage pool.  But our hosts aren't.  Ahh but the current version of virsh allows us to migrate virtual machines which aren't using shared storage.  Sweet.  But is an even reasonably recent version of virsh is available for Ubuntu?  No.  Poop.  Compile the new kvm from source?  Migrate to Debian over the weekend?  Spend zillions on vmware?  Ummmmmm.. no, no and..  no (funnily enough).

But we can do custom hack foo code (and so can you)!! :)

$ ./manage.vm.sh
This command needs to be run as root (use sudo)

$ sudo ./manage.vm.sh
usage: manage.vm.sh list: List all guests on the current host
usage: manage.vm.sh backup vmname: Shutdown, backup & restart a guest
usage: manage.vm.sh migrate vmname destination host="": Shutdown, copy, define remote, destroy local & startup remote

$ sudo ./manage.vm.sh migrate ansible feathertail
Considering migrating ansible to feathertail
 ..testing to see if we have the correct permissions on the remote host
 ..yay! connectivity with remote host established
Parsing KVM configuration for ansible
 ..assuming config file is at /etc/libvirt/qemu/ansible.xml
 ..attempting to determine datafile location
 ..ansible is configured to use /var/lib/libvirt/images/ansible/tmpfyi1SJ.qcow2
Checking to see if the guest is running
  !!WARNING   guest is still running, initiating shutdown.  Is an outage OK? .. ..enter L8 to verify:  L8
 ..acknowledging verification commencified
 ..shutting down, please wait  :) :) :) ansible now not running

Are you sure you want to proceed migrating ansible to feathertail? ..enter Q2 to verify: Q2

..and so on.  It actually works.  Does cool stuff like;
  • Automatically parses config files from /etc/libvirt/qemu/
  • Will migrate a vm with multiple vm datafiles
  • Renames local .xml and data files before undefining the vm.  Read; roll-back.
  • Prompts before shutting down a vm and again before migrating it.
  • Copies .xml and data file(s) remotely via rsync.
  • Defines the vm on the remote host and starts it.
Anyhow have a play and if you like it or have feedback or whatever, please let me know.

Logging the connection status of your ADSL router

Awhile back learning perl coincided with some issues i had with my ADSL.

  • Random disconnects
  • Unknown uptimes nor connection speeds
  • Unreliable connection speeds
  • Unable to reset the device via the command line
  • And a general feeling of being ripped off by the ISP.

So i hit it with the Perl hammer and produced 24k monolith which solves all that and more but will probably never work for anyone else :)  If that sounds like a challenge, then have a look at adsl-status on github.

$ ./adsl-status.sh
ADSL2 synced @~ 657KB/s (avg 662, max 747). Up 24238 mins (avg 8562, max 89341); 16 day 19 hr 57 min 21 sec.

$ ./adsl-status.sh --help
usage: adsl.sync[.dev].sh [--verbose|--silent|--help|--debug] | [--reset]

$ ./adsl-status.sh --verbose
verbose, adsl: clean_text_split = Annex Type ADSL2 -- Upstream 967600 -- Downstream 5374560 -- Elaspsed Time 16 day 19 hr 59 min 14 sec

verbose, adsl: adsl_annex_type = ADSL2
verbose, adsl: connection_uptime_in_seconds = 1454354
verbose, adsl: down_bits_per_second / up_bits_per_second (bps) = 5374560/967600
verbose, adsl: down_kilobits_per_second / up_kilobits_per_second (kbs) = 5375/968
verbose, adsl: down_megabits_per_second / up_megabits_per_second (mbs) ~ 5.5/1
verbose, adsl: down_kilobytes_per_second / up_kilobytes_per_second (KB/s) ~ 657/119
yada yada..

Starting out with github on Ubuntu

OK.  Ten mile high view.  Sometimes I write code, but alot of the code I write is too monolithic to post to my blog.  Seems to me I should use some kind of publicly accessible revision control system to check-in the code, and then link to that from my blog.  We already use subversion at work and some of my code lives there but allowing public access to that makes no sense.  Sometime code lives at home and that never even sees the work SVN system.  What would be cool is some system which allows me to control code from wherever i am, and yet allow public access to anyone.  Enter GIT and Github.

So if all the cool kids are using GIT, how do we get it installed, running and working so i can actually link to it?  Github have a very nice help system and i used that to do what i have done here;
  • Installed git on my ubuntu machine
  • created an account on github
  • logged into github
  • on github created a new repository called hooliowobbits/testing
  • on my machine i ran
    $ git clone https://github.com/hooliowobbits/testing.git
    Cloning into 'testing'...
    remote: Counting objects: 3, done.
    remote: Total 3 (delta 0), reused 0 (delta 0)
    Unpacking objects: 100% (3/3), done.
    $ cd testing/
    $ ls -lha 
    total 8.0K
    drwxr-xr-x 8 hoolio datarw 4.0K Mar  6 11:39 .git
    -rw-r--r-- 1 hoolio datarw   28 Mar  6 11:39 README.md
    $ cat README.md
    testing
    =======
    sandbox foo
    $ echo blah > blah.txt
    $ git add blah.txt
    $ git commit blah.txt
    [master 5f1522c] this is just a note i added when i first typed git commit blah
     1 file changed, 1 insertion(+)
     create mode 100644 blah.txt
    $ git push
    Username for 'https://github.com': hooliowobbits
    Password for 'https://hooliowobbits@github.com':
    To https://github.com/hooliowobbits/testing.git
       2ebbe5e..5f1522c  master -> master
    
    
  • Then i visited github again and i see my blah.txt sitting there :) 
Right now I can't presume to know much more about GIT and Github than this; but clearly this opens up a world of possibilites.  Suddenly with one command on my machine the world can see my code, use it, can comment on it, fork it whatever.

Now, let's code!

Internode quota from the Ubuntu command line

I have an Internode ADSL connection and I'm running Ubuntu Server (12.04). I wanted to be able to check my internet quota from the command line and i found a perl script to help do that, but there weren't quite enough instructions there for me to make it work. It wasn't too hard to fix though:

$ wget http://zwitterion.org/software/internode-quota-check/internode-quota-check
$ chmod +x internode-quota-check
$ mv internode-quota-check internode-quota-check.sh
$ sudo apt-get install libwww-mechanize-perl libreadonly-perl
$
$ ./internode-quota-check.sh man
you don't seem to have a ~/.fetchmailrc, so I'll prompt you.
To avoid extra dependencies, your password will be echoed.
Username: juliusroberts
Password: passwordhere
Run this command to create a ~/.fetchmailrc file:
echo '# poll mail.internode.on.net user "juliusroberts" password "passwordhere"' >> ~/.fetchmailrc
$ echo '# poll mail.internode.on.net user "juliusroberts" password "passwordhere"'>> ~/.fetchmailrc
$
$ ./internode-quota-check.sh
juliusroberts: 132.032 GB (88.0%) and 13.6 days (48.5%) left on 150 GB, 24 Mb/s plan.
$

So I then went and added  ./internode-quota-check.sh to the bottom of my ~/.bashrc file.  So now when i login to my server i see straight away how much internets i have left, yay :)

Monday, March 04, 2013

Use perl to check spamhaus status in Nagios

We had an issue where something on our internal network was tripping a SMTP spam filter at spamhaus.org.  We thought we fixed it once only to be bitten again a few months later and payslips from our payrol system were bouncing (bad).  As well as actually investigating the root cause, we created a nagios check to check spamhaus programatically.  Creating a custom nagios check is well documented on the nagios website.

#!/usr/bin/perl
#
# Quick perl script to check spamhaus to see if we're blocked again, see https://rt.wilderness.org.au:444/rt/Ticket/Display.html?id=73259
#
# This script returns values consistent with the nagios return code specification at http://nagiosplug.sourceforge.net/developer-guidelines.html#AEN76
# 0     OK          The plugin was able to check the service and it appeared to be functioning properly
# 1     Warning     The plugin was able to check the service, but it appeared to be above some "warning" threshold or did not appear to be working properly
# 2     Critical    The plugin detected that either the service was not running or it was above some "critical" threshold
# 3     Unknown     Invalid command line arguments were supplied to the plugin or low-level failures internal to the plugin 
#                       (such as unable to fork, or open a tcp socket) that prevent it from performing the specified operation. Higher-level errors 
#                       (such as name resolution errors, socket timeouts, etc) are outside of the control of plugins and should generally NOT be reported as UNKNOWN states.

use strict;
my $our_external_ip = "xxx.xxx.xxx.xxx"; 
my $exit_value=3;

# run the wget command and save it's output to the $results variable.
my $results=`/usr/bin/wget --random-wait -U mozilla -O /tmp/spamcheck.dat -o /tmp/spamcheck.log http://www.spamhaus.org/query/ip/$our_external_ip && grep $our_external_ip /tmp/spamcheck.dat`; 
chomp($results); 
#print $results."\n";

# check to see if we score RED ie; we're on a blocklist
if ($results =~ m/red/) {
    print "ALERT: Block found.  Check http://www.spamhaus.org/query/ip/$our_external_ip\n";
    $exit_value=2;
} 

# if we got this far, we saw no RED.  Check to see we at least get one GREEN.
elsif ($results =~ m/green/) {
    print "OK; We got a green and no red.\n";
    $exit_value=0; 
} 

# ALERT: We got no red AND no green; therefore there bust be some issue somewhere!
else {
    print "ALERT: No valid return codes detected; page-load/dns/internet/scripting issue?\n";
    $exit_value=3; 
}

#print "\nPerl hopes it's returning a \$exit_value of $exit_value\n\n";
exit $exit_value;

note that /tmp/spamcheck.* will need to be globally writable.

That then results in a nice web gui telling us all isn't well (again).  Happy with my work i told my boss who said "i don't like it, i want it to say OK", to which i replied, "i can do that ;)", and now you can too.  Happy automation :)




Thursday, February 28, 2013

All your servers are belong to Ansible.

So say you've got a few *NIX servers of various flavors, a dozen or two; it takes a day or so to add a new one to production, installing ntp, configuring your custom software repositories, configuring the various accounts it might need, installing ssh, adding it to the backup system, to the monitoring system etc etc.  Say you're getting over it.  Say you need to change your software repositories or your admin ssh keys.  Familiar?  enter server automation:


We'd heard about chef, and tried puppet; the uuber configuration management system which is great, so they say; on the 3rd or 4th incarnation :)  We wanted something a bit simpler something that avoided the monolithic client/server model, could be run anywhere (with git) and which used the SSH key auth we were already using.  It had to be able to manage groups of machines in a logical "idempotent" way.  Idempotent means you can apply a play which says "make it like thus" and if nothing needs to change, nothing is changed.  You can apply it again (and again) and not break anything.

So anyhow we found all of that in a free open source software project called Ansible;

"Orchestrate From Above.
Most software does not run on a single machine.
Ansible parallelizes complex multi-tier rollouts across app servers, databases, monitoring servers, and load balancers.."

after following the doco I had it up and running managing NTP on 25 servers within a day.  A good percentage of that was spent sorting out root ssh access (although sudo is ok too) and finding out what NTP is actually packaged as on centos vs ubuntu vs debian etc.

We've now moved on to managing users and ssh keys with Ansible and i can see this making a very significant difference for us.

Thursday, August 16, 2012

Using nwam to set a static ip on Open Indiana


Using Networking Automagic (NWAM) under Open Indiana is sweet.  It puts all those annoying Solaris network configuration elements into one sub-system, and then adds value by allowing us to have multiple network configurations and switch between them.

In NWAM parlance, an ncp is a Network Configuration Profile where you set the ip address, subnet etc and you can have as many of these as you like.  In order for DNS to work, you also have one or more locations (called a LOC) in which we set the search domain, dns servers etc and you can switch between them too.

When we needed to deploy a server for a remote office we simply created a ncp and a loc for that office, and switched to it just before turning the machine off.  This is how we did that.

First, determine the name of the network interface.  In this case nge0
$ ifconfig -a
lo0: flags=2001000849 mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000
nge0: flags=1000843 mtu 1500 index 5
        inet 10.10.1.44 netmask ffffff00 broadcast 10.10.1.255
Then configure a ncp for your physical network , and also a loc for the dns settings;
$ sudo /usr/sbin/nwamcfg
nwamcfg> create ncp 132DaveySt
nwamcfg:ncp:132DaveySt> create ncu phys nge0
Created ncu 'nge0'.  Walking properties ...
activation-mode (manual) [manual|prioritized]> prioritized 
enabled (true) [true|false]>
priority-group> 0
priority-mode [exclusive|shared|all]> shared
link-mac-addr>
link-autopush>
link-mtu>
nwamcfg:ncp:132DaveySt:ncu:nge0> end
Committed changes
nwamcfg:ncp:132DaveySt> create ncu ip nge0
Created ncu 'nge0'.  Walking properties ...
enabled (true) [true|false]>
ip-version (ipv4,ipv6) [ipv4|ipv6]>
ipv4-addrsrc (dhcp) [dhcp|static]> static
ipv4-addr> 10.10.1.44/24
ipv4-default-route> 10.10.1.254
ipv6-addrsrc (dhcp,autoconf) [dhcp|autoconf|static]> dhcp,autoconf,static
ipv6-addr>
ipv6-default-route>
nwamcfg:ncp:132DaveySt:ncu:nge0> end
Committed changes
nwamcfg:ncp:132DaveySt> end
nwamcfg> list
NCPs:
132DaveySt
Automatic
Locations:
Automatic
NoNet
User
nwamcfg> create loc 132DaveySt
Created loc '132DaveySt'. Walking properties ...
activation-mode (manual) [manual|conditional-any|conditional-all]>
enabled (false) [true|false]> true
nameservices (dns) [dns|files|nis|ldap]> dns
nameservices-config-file ("/etc/nsswitch.dns")>
dns-nameservice-configsrc (dhcp) [manual|dhcp]> manual
dns-nameservice-domain> natoffice.wilderness.org.au
dns-nameservice-servers> 10.10.1.6
dns-nameservice-search> natoffice.wilderness.org.au
nfsv4-domain>
ipfilter-config-file>
ipfilter-v6-config-file>
ipnat-config-file>
ippool-config-file>
ike-config-file>
ipsecpolicy-config-file>
nwamcfg:loc:132DaveySt> end
Committed changes
nwamcfg> end
and then we enable the NCP and the LOC and we're good to go.
$ sudo /usr/sbin/nwamadm enable -p ncp 132DaveySt
$ sudo /usr/sbin/nwamadm enable -p loc 132DaveySt

Wednesday, August 08, 2012

I made it to Hackaday! :) :)

It's probably shameless cross promotion, but having been a long time reader of HACKADAY, it's been one of my secret ambitions to be posted up there, along with the other tech-hackers-nerd-makers-whatever-you-wanna-callits. And with TINY, i made it. Hooray for me :)


Look, it's probably a bit self indulgent of me, but i just want to, you-know, hand out a bit of life advice so listen up; people! if you want to do something, then just go out there and do it! All you have to do is trust your instincts and follow your dreams and you too can create arcane nerd oddities and be celebrated for it! :) Thankyou Internet; you made my day today.  That's all, I'll stop now.

Monday, August 06, 2012

Logging multiple sensors to Cosm with Arduino

I'm no rocket scientist (congratulations NASA btw), but when I started out with my EtherTen (ethernet enabled arduino) i was amazed how easy it was to get internet data logging happening.

 

I first started with the example sketch in the Arduino IDE, but i fell down when i discovered that the example code only supports one sensor, and I wanted two (indoor and outdoor temp sensors).  Here's what i did to get two sensors running:
  1. Connected two thermistors using 10k pad resistors to my Arduino pins A0 and A1 as per the example.
  2. I created an account with cosm and had it generate me an api key etc.  Despite looking quite lovely, that site seems a little unintuitive, but stick with it; it will make sense after awhile.
  3. Create a new Arduino device/feed and copy the example sketch it provides you.  Note your API key and FEEDID.
  4. Create a new sketch in the Arduino IDE but instead of using their example (which will work fine for one sensor) use the sketch below.
  5. Substitute in your API key and FEEDID.  
  6. Change IPAddress ip(10,0,1,20); to suit you network.  You can use dhcp by simply following the layout from the example CosmClient sketch in the IDE.
  7. Finally change the name of your sensors from Inside, Outside, to something sane for you.
  8. Compile the code, upload to the Arduino etc and you should be good to go.
I have not yet however managed to get cosm to allow me to stack two data ranges on the same chart.

Sunday, August 05, 2012

Interfacing Atari 2600 Joystick to Arduino

Cleaning up the study the other day i found an old joystick from an Atari 2600.  So like everything else, I tried to connect it to my Arduino :)

The Arduino "button" example uses pull-down resistors, but after awhile i realized that approach wasn't going to work. This joystick switches to ground, meaning that any voltage going to say the Fire button will "dissapear" when the button is pressed. Same for each of the directions too, because this is a "digital" joystick, they're all just buttons inside the joystick.

So the answer is to measure what's going *into* the joystick, not what's coming out. For each of the joystick pins we send 5v into the joystick, and monitor that voltage. If there's 5v going in, then nothing has happened. But if that suddenly drops to near nothing, then a button has been pressed.

Figure 1: Current limiting resistors.

/*
 Interfacing Atari Joystick to Arduino:
 
 Returns output of Atari 2600 9 pin joystick to Arduino serial console.
 
       +---------> Right
       | +-------> Left
       | | +-----> Down
       | | | +---> Up 
       | | | | 
   _____________
 5 \ x o o o o / 1
    \ x o x o / 
   9 `~~~~~~~' 6
        |   |
        |   +----> Button
        +--------> Ground
 
 pinout via http://www.epanorama.net/documents/joystick/ataristick.html
 
 The circuit:
 * 5v through 10k resistor to each of the 5 joystick output pins
 * Arduino ground to Joystick ground
 * 5 data wires from joystick inputs to the arduino pins in the format:
    5V -> 10K resistor > Data to Arduino > Joystick Input
    
 created 2012 by Julius Roberts http://hooliowobbits.blogspot.com.au/
 
 This example code is in the public domain.
 */

// constants won't change. They're used here to set pin numbers:
const int FireButtonPin = 2;     
const int JoystickUpPin = 3;
const int JoystickDownPin = 4;
const int JoystickLeftPin = 5;
const int JoystickRightPin = 6;

void setup() {
  // initialize the Joystick pins as inputs:
  pinMode(FireButtonPin, INPUT);     
  pinMode(JoystickUpPin, INPUT);
  pinMode(JoystickDownPin, INPUT);
  pinMode(JoystickLeftPin, INPUT);
  pinMode(JoystickRightPin, INPUT);

  Serial.begin(9600);
}

void loop(){
  // read the state of the pushbutton value.  Because we're using a
  // pullup resistor, the value for each of the variables will normally 
  // be 1, and 0 when pressed.  This is the opposite of normal 
  // programming logic where 1 = true.  So we invert the variable using
  //  the map function  
  int FireButtonState = digitalRead(FireButtonPin); 
  FireButtonState = map(FireButtonState,0,1,1,0); 
  int JoystickUpState = digitalRead(JoystickUpPin); 
  JoystickUpState = map(JoystickUpState,0,1,1,0);
  int JoystickDownState = digitalRead(JoystickDownPin); 
  JoystickDownState = map(JoystickDownState,0,1,1,0);
  int JoystickLeftState = digitalRead(JoystickLeftPin); 
  JoystickLeftState = map(JoystickLeftState,0,1,1,0);
  int JoystickRightState = digitalRead(JoystickRightPin);  
  JoystickRightState = map(JoystickRightState,0,1,1,0);

  // output the setting to the serial console
  Serial.print("Fire: "); Serial.print(FireButtonState);
  Serial.print(" Up: "); Serial.print(JoystickUpState);
  Serial.print(" Down: "); Serial.print(JoystickDownState);
  Serial.print(" Left: "); Serial.print(JoystickLeftState);
  Serial.print(" Right: "); Serial.println(JoystickRightState);
}

Friday, August 03, 2012

Managing many LEDs on Arduino

Arguably the most simple Arduino project of all is the "blink" sketch which comes pre-installed the Arduino software (examples > basics > blink).  Some Arduino boards even have a surface mount LED already connected to pin 13 so you can do this sketch without any additional hardware.

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */
 
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
}

And once you get that far, it's not much more difficult to blink multiple lights in some sequence, using a separate arduino pin for each LED you wish to light, and some logic defining which to blink when, or which delay etc.  The "problem" is that as you add more LEDs to such a project each LED requires a dedicated pin. 10 LEDs = 10 pins, 15 LEDs = 15 pins etc, so that approach reasonably scales up to about 16 pins, give or take.  After that you need a smarter way to address the LEDs.  One way is to buy a fancy LED array or the alternative is to make your own.  I chose the latter of course :)

The video below to shows how to wire a circuit in order to allow your Arduino to drive more LEDs than you have pins, and also how to use a technique called multiplexing to avoid some of the compromises in not actually having one pin per LED.  I use this method to wire the circuit board in TINY (TINY Is Not space invaYders) which features a 5x5 array of 25 LEDs using "only" 10 pins.


TINY (This Is Not space invaYders)

Or "Yet Another Arduino Blinken Light Thing" :)

So we were chatting on the TASLUG (Tasmanian Linux Users Group) mailing list talking about a new hackerspace development and whatnot when one of the fellows said he hoped that it didn't become a club of Arduino fanboys.  I said what's up with Arduino, it's awesome, and he said there's far too much boring blinkenlights and not enough actual cool stuff.  It was a bit tongue in cheek, but regardless, my nerd pride was offended and I managed to interpret it as a challenge :)   A month or so later and I had created TINY:


I'm pretty sure nerd etiquette precludes me from proclaiming success, but it did have an awesome time doing it.  I am curious though, what do you think? ;)
/*
  Space Invaders
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */
 
// pin assignments
int annodes0 = 2;int annodes1 = 3;int annodes2 = 4;int annodes3 = 5;int annodes4 = 6;
//pin 7 is spare
int cathodes0 = 8;int cathodes1 = 9;int cathodes2 = 10;int cathodes3 = 11;int cathodes4 = 12;
int IndicatorLED = 13;
int InputPot = A0;
int C_ButtonPin = 18; //ie analog4, in use as a digital pin
int C_SpeakerPin = 19; //ie analog5, in use as a digital pin

int mycursor=2;  // set initial position of the cursor
//int difficulty = 1; // where 1 is easy and 3 is hard.

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the pins
  pinMode(annodes0, OUTPUT); pinMode(annodes1, OUTPUT); pinMode(annodes2, OUTPUT); pinMode(annodes3, OUTPUT); pinMode(annodes4, OUTPUT);
  pinMode(cathodes0, OUTPUT); pinMode(cathodes1, OUTPUT); pinMode(cathodes2, OUTPUT); pinMode(cathodes3, OUTPUT); pinMode(cathodes4, OUTPUT);
  pinMode(IndicatorLED, OUTPUT); 
  pinMode(C_ButtonPin, INPUT);  pinMode(InputPot, INPUT);
 
  Serial.begin(9600);
}

void loop() {
   //basicMultiplexing(); 
   //SoundEffectGenViaPot();  
   //MakeASmiley();   
   //lightled(6,2000); delay(2000);
   //int buttonState = digitalRead(C_ButtonPin); if (buttonState == HIGH) { sound(7); }  
   
   int difficulty = introSequence(); delay(200);
   SnotVaders(difficulty);     delay(200);
   MakeASmiley();   delay(1000);
}

int introSequence(){
   int difficultyX = SimpleVelocityLoop();
   //int difficulty = map(difficultyX, 0, 1023, 3, 1); 
   int difficulty = 0;
   if (difficultyX < 315) { difficulty = 3; }
   else if (difficultyX > 648 ) { difficulty = 1; }
   else { difficulty = 2; }
      
   Serial.print("difficultyX = "); Serial.println(difficultyX); 
   Serial.print("difficulty = "); Serial.println(difficulty); 

   delay(200);
   int mydelay=10;
   for (int i = 1; i <= 25; i++) {
      sound(1);
      lightled(i,mydelay); delay(mydelay);       
      lightled(25-i,mydelay); delay(mydelay);  
   }
   sound(7);  
   delay(400);
   return difficulty;
}

void SnotVaders(int difficulty){
   // (number of displaycycles per tick will be determined by gameroundtickspeed[v_round].
   // the number of rounds is determined by the length of gameroundtickspeed[].
   // we determine the ticks per round based on the difficulty :)
   int gameroundtickspeed[]= {0,0,0,0,0,0,0,0,0};
   switch (difficulty){
      case 1: // easy
         { int gameroundtickspeedX[]= {28,22,23,20,21,17,17,17,17};  for (int z = 0; z < 9; z++) { gameroundtickspeed[z]=gameroundtickspeedX[z];} break; } 
      case 2: // medium
         { int gameroundtickspeedX[]= {20,20,20,19,18,14,14,14,11}; for (int z = 0; z < 9; z++) { gameroundtickspeed[z]=gameroundtickspeedX[z];} break; } 
      case 3: // hard
         { int gameroundtickspeedX[]= {7,7,7,7,7,7,7,7,7}; for (int z = 0; z < 9; z++) { gameroundtickspeed[z]=gameroundtickspeedX[z];} break; } 
   }
   
   int rounds = sizeof(gameroundtickspeed)/sizeof(int);
   int shipLocation=1; int notdead=1; int idealFlikerRate=24;
   int buttonStateToggle = 1; int baddieDetails[] = {0,0,0,0,0};
    
   // play successive rounds, each terminating when either all baddies or the player dies
   for (int round = 1; round <= rounds; round++){   
      // output the round to the serial console for debug
      //Serial.print(" round = "); Serial.println(round);
      
      // baddies move closer to the player each round.  
      // and they will do that faster depending on the round number.
      // if they hit the player, then the player dies, game over etc.
      
      // we define baddie starting location based on the round.
      // note that locations < 1 is "no location" and wont be displayed at all.  
      
      // i was unable to have the switch populate baddieDetails[] directly, so that's why
      // we have this odd baddieDetails[z]=locations[z] arrangement.
      switch (round) {
         case 1: { 
            int locations[]= {15, -1, -1, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;
         case 2: { 
            int locations[]= {5, 25, -1, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;       
         case 3: { 
            int locations[]= {5, 14, 25, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;                       
         case 4: { 
            int locations[]= {4, 15, 24, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 5: { 
            int locations[]= {4, 10, 15, 20, 24};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 6: { 
            int locations[]= {3, 9, 15, 19, 23};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 7: { 
            int locations[]= {4, 8, 15, 18, 24};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 8: { 
            int locations[]= {3, 8, 15, 18, 23};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
            
      } int baddieDetailsarraylen = sizeof(baddieDetails)/sizeof(int); 
              
      while(notdead) {
         // grunt noise
         sound(1);  
         
         // update the display X times to do the flicker thing
         for (int displaycycle = 1; displaycycle <= gameroundtickspeed[round-1]; displaycycle++) {
            // we plot LEDs once per displaycycle.  we do that repeatedly to give us a picture.
            // (number of displaycycles per tick will be determined by gameroundtickspeed[v_round]
            // each plotXX() function will illuminate and also extinguish it's own LEDs
            // we want this to minimise the number of simultaneously lit leds; ie flicker.)
            
            // first plot the baddies based on baddieDetails[] and check to see if they're all dead
            notdead = PlotBaddiesAndDetectAllDead(baddieDetails, baddieDetailsarraylen, idealFlikerRate); 
            if (notdead == 0) { break; } //terminate processing the while loop
            
            // check to see if any of the baddies have hit the ground, if so terminate the game.
            int Zinstruction[] = {1, 6, 11, 16, 21};  
            for(int z = 0; z < baddieDetailsarraylen; z++) { 
               for(int q = 0; q < 5; q++){
                  if (baddieDetails[z] == Zinstruction[q]){
                     // then a baddie has hit the ground, play wah wah wahhh and end
                     sound(5); delay (500); software_Reset(); 
            }  }  }
            
            // check to see if the player is shooting and if so, plot the laser and return the path it took.
            // the toggle stuff is to make sure they're not just holding the fire button
            int buttonState = digitalRead(C_ButtonPin); 
            if (buttonState == HIGH) { 
               if (buttonStateToggle) {
                  PlotShootAndDetectHit(baddieDetails, baddieDetailsarraylen, shipLocation);
                  buttonStateToggle = 0;
               }
            } else { buttonStateToggle = 1; }
            
            // move ship and return our current location
            shipLocation=PlotAndControlShipV2(idealFlikerRate); //ie; PlotAndControlShip(delay)
                     
            // output debug stuff
            //Serial.print(" dc = "); Serial.print(displaycycle); 
            //Serial.print(" rounds = "); Serial.print(rounds); 
            //Serial.print(" round = "); Serial.println(round);
            //Serial.print(" shipLocation = "); Serial.println(shipLocation);
         } // end of displaycycle
         
         // make the baddies move closer to the player
         for(int z = 0; z < baddieDetailsarraylen; z++) { 
         baddieDetails[z]--; 
         Serial.print(" baddieDetails["); Serial.print(z); Serial.print("] "); Serial.println( baddieDetails[z]);
         }         
         //}
       } // end of roundlengthcounter, ie end of round
       
       delay(500); sound(4); delay(1000); //delay between rounds
       notdead = 1;
   } // end of round, ie end of the game.
   
   sound(6); // play endgame music   
}

void software_Reset() { 
   // Restarts program from beginning but does not reset the peripherals and registers
   // taken from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1246541553
   asm volatile ("  jmp 0");  
}

void MakeASmiley(){
   int instructions[]= {9, 19, 2, 6, 11, 16, 22}; 
   //int instructions[]= {10, 20, 14, 22, 21, 16, 11, 6, 1, 2}; //froggy
   int instructionsArraylen = sizeof(instructions)/sizeof(int); 
   
   for (int i = 0; i < instructionsArraylen; i++){
      lightled(instructions[i],350);      
   }
   delay(200);   
   for (int flickercounter = 0; flickercounter < 1008 ; flickercounter ++) {
      // iterate through the array, starting at element 0
      for (int i = 0; i < instructionsArraylen; i++){
         lightled(instructions[i],1);
         //delay(100);
      }
   }
   delay(1000);
}

int PlotBaddiesAndDetectAllDead(int baddieDetails[], int baddieDetailsarraylen, int mydelay){
   int deadBaddieCounter=0;
   // note the last element in the array is arraylen -1.
   int lastarrayelement = baddieDetailsarraylen - 1;
                
   // iterate through the array, starting at element 1, not 0.  and while we're at it
   // we'll calculate the score too
   int score = 0;
   for (int i = 0; i < baddieDetailsarraylen; i++){
      // blink the leds according to the baddieDetails      
      if (baddieDetails[i] < 0){ // this is a dead baddie, so don't plot it.  instead count it.
         deadBaddieCounter++;
         if (deadBaddieCounter == baddieDetailsarraylen) { // all baddies are dead
            score = score + baddieDetails[i];  score = score * -1; score = 5 - score;
            Serial.print(" score = "); Serial.println( score);
            return 0;
         }
      } else {
         lightled(baddieDetails[i],mydelay);
      }
   }     
}

void PlotShootAndDetectHit(int baddieDetails[], int baddieDetailsarraylen, int shipLocation){
   //note this assumes that the ship won't leave the planet surface and that the 
   //planet surface is defined explicityly as 1, 6, 11, 16, 21.  if that changes 
   //then this will look odd.
   int shootdelay = 12;
  
   // to start with, simply play the shoot noise
   sound(2);  //shoot
   
   // then take the ship location, and for each one, calculate the laser trajectory
   // each led upon that trajectory is called laserLocation.  when we have that laserLocation
   // also iterate through the baddieDetails array to see if that laserLocation coincides with 
   // where a baddie is, if so, play the explode noise.
   switch(shipLocation){ // this will send the laser towards the baddies :)
      case 1:
         for(int laserLocation=2; laserLocation <=5; laserLocation++){ // plot the laser trajectory
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it                 
         }  }  }   
         break;
      case 6:
        for(int laserLocation=7; laserLocation <=10; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;
      case 11:
          for(int laserLocation=12; laserLocation <=15; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;
      case 16:
          for(int laserLocation=17; laserLocation <=20; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;      
      case 21:
          for(int laserLocation=22; laserLocation <=25; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;
    }
} 

int PlotAndControlShipV2(int mydelay){
  // planet surface 
  int instruction[] = {1, 6, 11, 16, 21};  
  // mycursor is a globally declared variable which stores where in the *instruction array* the ship
  // is currently residing.  it should not be confused with shipLocation which stores the LEDid of the 
  // ship location.  ie shipLocation = instruction[mycursor]
  
   int rawLocation = analogRead(InputPot); int location = map(rawLocation, 0, 1025, 0, 5);
  
   //Serial.print("rawLocation = "); Serial.print(rawLocation);  Serial.print(" location = "); Serial.println(location);

   lightled(instruction[location],mydelay);  
  
   return instruction[location];
}

void sound(int soundeffect) {
   switch( soundeffect ) {
      case 1: {//grunt
         tone(C_SpeakerPin, 17, 250);
      } break;
      case 2: {//shoot
         for (int z = 0; z < 1; z++){
            tone(C_SpeakerPin, 600, 80);      delay(40);
            tone(C_SpeakerPin, 100, 40);       delay(10);
         } 
      } break;
      case 3: {//explode
         //basically random-ish noise descending in frequency
         int freqbase=480; // where to start 
         for(int i=0; i < 15; i++){ // how many individual noises in the effect
            freqbase = freqbase - 20; // how much lower should each noise go?
            int frequency = random(freqbase-30,freqbase); //what exact freqency to plot
            int mydelay = random(30,150); // how long should each noise go for
            tone(C_SpeakerPin, frequency, mydelay); 
            Serial.print("frequency = "); Serial.print(frequency);  Serial.print(" mydelay = "); Serial.println(mydelay);
         } 
      } break;
      case 4: {// end round
         tone(C_SpeakerPin, 300, 300);delay(150);
         tone(C_SpeakerPin, 600, 100);delay(300);
         tone(C_SpeakerPin, 300, 300);delay(150);
         tone(C_SpeakerPin, 600, 800);delay(300);
      } break;
      case 5: {//you loose
         for (int z = 0; z < 1; z++){
            tone(C_SpeakerPin, 300, 80);      delay(800);
            tone(C_SpeakerPin, 275, 80);      delay(800);
            tone(C_SpeakerPin, 260, 80);      delay(800);
            for (int i = 0; i < 30; i++) {
               tone(C_SpeakerPin, 245, 15);     delay(15);  
               tone(C_SpeakerPin, 255, 15);     delay(15);                  
            }
            tone(C_SpeakerPin, 100, 40);       delay(10);
         } 
      } break;
      case 6: {//you win!
         // the song to play at the truimphant conclusion :)
         char *song = "20thCenFoxShort:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p";
         play_rtttl(song);   
      } break;
      case 7: {// game start
         //basically random-ish noise ascending in frequency
         int freqbase=180; // where to start 
         for(int i=0; i < 30; i++){ // how many individual noises in the effect
            freqbase = freqbase + 15; // how much lower should each noise go?
            int frequency = random(freqbase-30,freqbase); //what exact freqency to plot
            int mydelay = random(30,150); // how long should each noise go for
            tone(C_SpeakerPin, frequency, mydelay); 
            Serial.print("frequency = "); Serial.print(frequency);  Serial.print(" mydelay = "); Serial.println(mydelay);  
         }
         tone(C_SpeakerPin, 100, 15);     delay(250);                  
         for (int i = 0; i < 2; i++) {
            for (int i = 0; i < 4; i++) {
               tone(C_SpeakerPin, 690, 100);     delay(20);  
               tone(C_SpeakerPin, 640, 100);     delay(20);  
            }
            tone(C_SpeakerPin, 100, 15);     delay(100);                  
         }
      } break;
   }
}

void lightled(int LEDid, int ondelay) {
  //Serial.print(" led = "); Serial.print(LEDid); Serial.print("  ondelay = "); Serial.println(ondelay);
  switch (LEDid) {
    case 0:
      //all off
      digitalWrite(cathodes0, LOW); digitalWrite(annodes0, LOW); digitalWrite(cathodes1, LOW); digitalWrite(annodes1, LOW);
      digitalWrite(cathodes2, LOW); digitalWrite(annodes2, LOW); digitalWrite(cathodes3, LOW); digitalWrite(annodes3, LOW);
      digitalWrite(cathodes4, LOW); digitalWrite(annodes4, LOW); break;
    case 1: //c0a0
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes0, HIGH); break;
    case 2: //c0a1
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes1, HIGH); break;
    case 3: //c0a2
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes2, HIGH); break;
    case 4: //c0a3
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes3, HIGH); break;      
    case 5: //c0a4
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes4, HIGH); break;
    case 6: //c1a0
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes0, HIGH); break;
    case 7: //c1a1
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes1, HIGH); break;
    case 8: //c1a2
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes2, HIGH); break;
    case 9: //c1a3
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes3, HIGH); break;
    case 10: //c1a4
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes4, HIGH); break;
    case 11: //c2a0
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes0, HIGH); break;
    case 12: //c2a1
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes1, HIGH); break;
    case 13: //c2a2
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes2, HIGH); break;
    case 14: //c2a3
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes3, HIGH); break;
    case 15: //c2a4
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes4, HIGH); break;
    case 16: //c3a0
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes0, HIGH); break;
    case 17: //c3a1
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes1, HIGH); break;
    case 18: //c3a2
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes2, HIGH); break;
    case 19: //c3a3
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes3, HIGH); break;
    case 20: //c3a4
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes4, HIGH); break;
    case 21: //c4a0
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes0, HIGH); break;
    case 22: //c4a1
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes1, HIGH); break;
    case 23: //c4a2
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes2, HIGH); break;
    case 24: //c4a3
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes3, HIGH); break;
    case 25: //c4a3
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes4, HIGH); break;
    case 88: //do nothing.
      break;  
    case 100:
      //all on
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes0, HIGH); digitalWrite(cathodes1, HIGH); digitalWrite(annodes1, HIGH);
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes2, HIGH); digitalWrite(cathodes3, HIGH); digitalWrite(annodes3, HIGH);
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes4, HIGH);    
      break;      
    default: // if nothing else matches, do the default
      //Serial.print("ERROR LEDid = "); Serial.println(LEDid); alloff(); allon(); delay(1500);
      break;
  }
  //delay a sec before resuming
  delay(ondelay);  alloff(); 
}

void alloff(){
  // turn everything off for contrast
  digitalWrite(cathodes0, LOW);  digitalWrite(cathodes1, LOW);  digitalWrite(cathodes2, LOW);  digitalWrite(cathodes3, LOW);  digitalWrite(cathodes4, LOW);
  digitalWrite(annodes0, LOW);  digitalWrite(annodes1, LOW);  digitalWrite(annodes2, LOW);  digitalWrite(annodes3, LOW);  digitalWrite(annodes4, LOW); 
  digitalWrite(IndicatorLED, LOW);
}

void allon(){
  // turn everything off for contrast
  digitalWrite(cathodes0, HIGH);  digitalWrite(cathodes1, HIGH);  digitalWrite(cathodes2, HIGH);  digitalWrite(cathodes3, HIGH);  digitalWrite(cathodes4, HIGH);
  digitalWrite(annodes0, HIGH);  digitalWrite(annodes1, HIGH);  digitalWrite(annodes2, HIGH);  digitalWrite(annodes3, HIGH);  digitalWrite(annodes4, HIGH); 
  digitalWrite(IndicatorLED, HIGH);
}

void SoundEffectGenViaPot(){
 //generate sounds using input pot
  int buttonState = digitalRead(C_ButtonPin);
  int frequencyX = analogRead(InputPot); int frequency = map(frequencyX, 0, 1023, 1, 6000);
   if (buttonState == HIGH) { 
      //tone(C_SpeakerPin, frequency, 200);
      tone(C_SpeakerPin, frequency, 40);      //delay(10);
      tone(C_SpeakerPin, frequency-200, 40);      //delay(10);
      tone(C_SpeakerPin, frequency, 40);      //delay(10);
      //tone(C_SpeakerPin, 100, 40);       delay(5);
  }
  Serial.print("frequency = "); Serial.println(frequency);
}

void  basicMultiplexing(){
  // for our matrix 24 is a good delay, but more leds means more flicker.  simple.
  int ondelayX = analogRead(InputPot); int ondelay = map(ondelayX, 0, 1023, 0, 100);

  for (int i = 1; i <= 25; i++){lightled(i,ondelay);}
  Serial.print("ondelay = "); Serial.println(ondelay);
}

int PlotAndControlShipVelocity(int mydelay){
  // planet surface 
  int instruction[] = {1, 6, 11, 16, 21};  
  // mycursor is a globally declared variable which stores where in the *instruction array* the ship
  // is currently residing.  it should not be confused with shipLocation which stores the LEDid of the 
  // ship location.  ie shipLocation = instruction[mycursor]
  
  // note the last element in the array is arraylen -1 !!!!
  int arraylen = sizeof(instruction)/sizeof(int); int lastarrayelement = arraylen - 1;
  // determine the position of the pot and assign a velocity to it
  int velocityX= analogRead(InputPot); int velocity = map(velocityX, 0, 1023, -150, 150);
  // set the cursor to increment or decrement depending on the velocity, note the deadzone
  if (velocity > 15) { mycursor++;} 
  if (velocity < -15) {mycursor--;}
  
  // what do we do when they reach the edge of the play area?  to wrap or not to wrap?
  // wrap:
  // if (mycursor < 0) { mycursor = lastarrayelement; } if (mycursor > lastarrayelement) { mycursor = 0; }
  // not wrap:
  if (mycursor < 0) { mycursor = 0; } if (mycursor > lastarrayelement) { mycursor = lastarrayelement; }
  
  //Serial.print("mycursor = "); Serial.print(mycursor);  Serial.print(" mydelay = "); Serial.print(mydelay);  Serial.print(" velocity = "); Serial.print(velocity); 
       
  // blink the leds according to the instructions
  lightled(instruction[mycursor],mydelay);
  
  //ie shipLocation = instruction[mycursor]  
  return instruction[mycursor];
}

int SimpleVelocityLoop(){
   // define the instructions for the array and the delay for led illumination
   //int instruction[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,0,100,0,100}; //test
   //int instruction[] = {1, 6, 11, 16, 21, 22, 23, 24, 25, 20, 15, 10, 5, 4, 3, 2, 7, 12, 17, 18, 19, 14, 9, 8, 13, 7}; // spiral and exit
   int instruction[] = {1, 6, 11, 16, 21, 22, 23, 24, 25, 20, 15, 10, 5, 4, 3, 2}; // outside loop
   //int instruction[] = {1, 6, 7, 11, 16, 12, 21, 22, 17, 23, 24, 18, 25, 20, 19, 15, 10, 14, 5, 4, 9, 3, 2, 8}; // 2 square loop

   // and then simply iterate through the instruction array
   int i; int arraylen = sizeof(instruction)/sizeof(int);
   
   int mydelayX = 0;
   int buttonState = 0;
   
   while(buttonState != HIGH){ 
      for (i = 0; i < arraylen; i = i + 1) {
         buttonState = digitalRead(C_ButtonPin); if (buttonState) { break; }
         // delay is used for setting both the ON and OFF durations
         mydelayX= analogRead(InputPot); int mydelay = map(mydelayX, 0, 1023, 4, 200);  
         // blink the leds according to the instructions
         // sound(1);
         lightled(instruction[i],mydelay);
         // turn everything off and wait a sec (before re-reading the instructinos and starting again etc)
         alloff(); delay(mydelay/2);
         //Serial.print("mydelayX = "); Serial.print(mydelayX);
      }   
   }
   return mydelayX;
}

void play_rtttl(char *p) {
  // this function based on the LCA 2012 leo stick tunes collaborative effort at https://gist.github.com/1800871
    
  //char *song = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c";
  //char *song = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f";
  //char *song = "20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b";
  //char *song = "Canon:d=4,o=5,b=80:8d,8f#,8a,8d6,8c#,8e,8a,8c#6,8d,8f#,8b,8d6,8a,8c#,8f#,8a,8b,8d,8g,8b,8a,8d,8f#,8a,8b,8f#,8g,8b,8c#,8e,8a,8c#6,f#6,8f#,8a,e6,8e,8a,d6,8f#,8a,c#6,8c#,8e,b,8d,8g,a,8f#,8d,b,8d,8g,c#.6";
  //char *song = "StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6";
  //char *song = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#";
  //char *song = "Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#";
  //char *song = "LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#";
    
  #define isdigit(n) (n >= '0' && n <= '9')
   
  #define NOTE_B0 31
  #define NOTE_C1 33
  #define NOTE_CS1 35
  #define NOTE_D1 37
  #define NOTE_DS1 39
  #define NOTE_E1 41
  #define NOTE_F1 44
  #define NOTE_FS1 46
  #define NOTE_G1 49
  #define NOTE_GS1 52
  #define NOTE_A1 55
  #define NOTE_AS1 58
  #define NOTE_B1 62
  #define NOTE_C2 65
  #define NOTE_CS2 69
  #define NOTE_D2 73
  #define NOTE_DS2 78
  #define NOTE_E2 82
  #define NOTE_F2 87
  #define NOTE_FS2 93
  #define NOTE_G2 98
  #define NOTE_GS2 104
  #define NOTE_A2 110
  #define NOTE_AS2 117
  #define NOTE_B2 123
  #define NOTE_C3 131
  #define NOTE_CS3 139
  #define NOTE_D3 147
  #define NOTE_DS3 156
  #define NOTE_E3 165
  #define NOTE_F3 175
  #define NOTE_FS3 185
  #define NOTE_G3 196
  #define NOTE_GS3 208
  #define NOTE_A3 220
  #define NOTE_AS3 233
  #define NOTE_B3 247
  #define NOTE_C4 262
  #define NOTE_CS4 277
  #define NOTE_D4 294
  #define NOTE_DS4 311
  #define NOTE_E4 330
  #define NOTE_F4 349
  #define NOTE_FS4 370
  #define NOTE_G4 392
  #define NOTE_GS4 415
  #define NOTE_A4 440
  #define NOTE_AS4 466
  #define NOTE_B4 494
  #define NOTE_C5 523
  #define NOTE_CS5 554
  #define NOTE_D5 587
  #define NOTE_DS5 622
  #define NOTE_E5 659
  #define NOTE_F5 698
  #define NOTE_FS5 740
  #define NOTE_G5 784
  #define NOTE_GS5 831
  #define NOTE_A5 880
  #define NOTE_AS5 932
  #define NOTE_B5 988
  #define NOTE_C6 1047
  #define NOTE_CS6 1109
  #define NOTE_D6 1175
  #define NOTE_DS6 1245
  #define NOTE_E6 1319
  #define NOTE_F6 1397
  #define NOTE_FS6 1480
  #define NOTE_G6 1568
  #define NOTE_GS6 1661
  #define NOTE_A6 1760
  #define NOTE_AS6 1865
  #define NOTE_B6 1976
  #define NOTE_C7 2093
  #define NOTE_CS7 2217
  #define NOTE_D7 2349
  #define NOTE_DS7 2489
  #define NOTE_E7 2637
  #define NOTE_F7 2794
  #define NOTE_FS7 2960
  #define NOTE_G7 3136
  #define NOTE_GS7 3322
  #define NOTE_A7 3520
  #define NOTE_AS7 3729
  #define NOTE_B7 3951
  #define NOTE_C8 4186
  #define NOTE_CS8 4435
  #define NOTE_D8 4699
  #define NOTE_DS8 4978  
  #define OCTAVE_OFFSET 0
  
  int notes[] = { 0,
  NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
  NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
  NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
  NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7
  };
    // Absolutely no error checking in here
  
    byte default_dur = 4;
    byte default_oct = 6;
    int bpm = 63;
    int num;
    long wholenote;
    long duration;
    byte note;
    byte scale;
  
    // format: d=N,o=N,b=NNN:
    // find the start (skip name, etc)
  
    while(*p != ':') p++; // ignore name
    p++; // skip ':'
  
    // get default duration
    if(*p == 'd'){
      p++; p++; // skip "d="
      num = 0;
      while(isdigit(*p)){
        num = (num * 10) + (*p++ - '0');
      }
      if(num > 0) default_dur = num;
      p++; // skip comma
    }
  
  
    // get default octave
    if(*p == 'o'){
      p++; p++; // skip "o="
      num = *p++ - '0';
      if(num >= 3 && num <=7) default_oct = num;
      p++; // skip comma
    }
  
    // get BPM
    if(*p == 'b'){
      p++; p++; // skip "b="
      num = 0;
      while(isdigit(*p)){
        num = (num * 10) + (*p++ - '0');
      }
      bpm = num;
      p++; // skip colon
    }
    
    // BPM usually expresses the number of quarter notes per minute
    wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds)
    
    // now begin note loop
    while(*p){
      // first, get note duration, if available
      num = 0;
      while(isdigit(*p)){
        num = (num * 10) + (*p++ - '0');
      }
      
      if(num) duration = wholenote / num;
      else duration = wholenote / default_dur; // we will need to check if we are a dotted note after
  
      // now get the note
      note = 0;
      
      switch(*p) {
        case 'c':
          note = 1;
          break;
        case 'd':
          note = 3;
          break;
        case 'e':
          note = 5;
          break;
        case 'f':
          note = 6;
          break;
        case 'g':
          note = 8;
          break;
        case 'a':
          note = 10;
          break;
        case 'b':
          note = 12;
          break;
        case 'p':
        default:
          note = 0;
      }
      p++;
  
      // now, get optional '#' sharp
      if(*p == '#'){
        note++;
        p++;
      }
  
      // now, get optional '.' dotted note
      if(*p == '.'){
        duration += duration/2;
        p++;
      }
    
      // now, get scale
      if(isdigit(*p)){
        scale = *p - '0';
        p++;
      }
      else{
        scale = default_oct;
      }
  
      scale += OCTAVE_OFFSET;
  
      if(*p == ',')
        p++; // skip comma for next note (or we may be at the end)
  
      // now play the note
  
      if(note){
        digitalWrite(C_SpeakerPin, HIGH);
        int danFreq;
        float danDur;
        danFreq = notes[(scale - 4) * 12 + note];
        danDur = 1000000 / danFreq;
        unsigned long start = millis();
        while (millis() - start <= duration) {
            digitalWrite(C_SpeakerPin, HIGH);
            delayMicroseconds(danDur);
            digitalWrite(C_SpeakerPin, LOW);
            delayMicroseconds(danDur);
        }
        digitalWrite(C_SpeakerPin, LOW);
      }
      else{
        delay(duration);
      }
    }
}


Tuesday, July 31, 2012

Posting code to your blogger blog

http://oneqonea.blogspot.com.au/2012/04/how-do-i-add-syntax-highlighting-to-my.html

^^ worked for me, had it up and running in a couple of mins
/*
  This Is Not space invaYders
  This example code is in the public domain, etc, have fun :)
 */
 
// pin assignments
int annodes0 = 2;int annodes1 = 3;int annodes2 = 4;int annodes3 = 5;int annodes4 = 6;
//pin 7 is spare
int cathodes0 = 8;int cathodes1 = 9;int cathodes2 = 10;int cathodes3 = 11;int cathodes4 = 12;
int IndicatorLED = 13;
etc.. Cool eh? :) to make some code look all neat like that you just have to use HTML view within the blogger interface, and put it between the pre tags as follows:
The "brushes" allow you to define the type of code and therefore which syntax gets highlighted etc.  In this case I've used the cpp brush but there are heaps of others documented at the authors website here. 

In some cases when the pre method doesn't work (such as when it gets confused over the less than character <) you can instead escape your code with the following. Note also this code also features the argument to have your code auto-hidden; great for long posts:

Monday, May 22, 2006

"..Savor the warmth and joy..

..that result from a calmer mind. After a while your thoughts will become like a peaceful river. If you practice regularly, eventually your mind will easily become serene, like a calm ocean. Whenever new thoughts arise, like waves raised by the winds, do not be bothered by them. They will soon dissolve back into the ocean.." - Matthieu Ricard

Dregnaughts

Dregnaught
\Dre*gnor*gt\, n. An popular Vignette** encapsulating key element(s) of a larger piece of contemporary media, where propagated exclusively via computer and networks (see Internet). Often showing cruel and unusual pain and feats of cunning trickery in cars, and both.
**(Vignette: (n.) A short scene or incident, as from a movie.)

Where do words come from?

I assert that a word is the formalisation of a concept; a handle on a mental construct, propogated by common use, then perhaps formal acceptance into language. A perception of reality based language and layers of conceptual understanding is wholly limited by these concepts. That they should form the foundation for what seems to be a popular perception of reality seems to me to be wholly lacking.

Consider a bookshop with ten thousand titles from gardening to philosophy through car mechanics and ancient translations.. then perhaps consider all the books in all the world which have ever been, maybe billions of tomes stacked so high as to block out the sun.. consider every single attemt to ever encapsulate even the most minute fragment of the most subjective element of our reality in words and ask; has that task actually ever been achieved?

..then consider what it is to be "knowledgable". Perhaps it is simply the biggest, most ego-centric, self-rightious myth ever known..

Monday, May 08, 2006

Peace in the Peace Movement

"...Contributions to peace are built upon innate goodness. Deep inside we must recognize that while we are different from those who go to and support war, different from those who let HIV/AIDS and hunger continue unchecked and who commit the genocide of African and other peoples of color, we are complicit in that arrogance by couching our expressions of protest in a language of fear and hatred, a language that lets our opposition point to our hypocrisy and makes some people of peace feel unable to participate in a movement that cultivates as much aggression as the events and policies we are striving to oppose..." - boona cheema