Archive for July, 2016

derpderp – Autonomous arduino-based robot

July 15, 2016

Here is yet another autonomous little robot that rolls around avoiding walls, this one was also made for a hackathon much like the venerable wheelchair robot.  The difference here is that insistence that code be developed on an SBC slowed development to the point that many features had to be dropped.  The point of this robot is to venture into unknown territory and send back sensor readings (distance, temperature, humidity, light level, barometric pressure) so you don’t have to go in blind.  The need for autonomy come from us not having a camera with which we could drive the robot.  The goals as best as I can remember:

  • autonomous exploring (drive forward, avoid walls)
  • wireless telemetry
  • large array of sensors
  • manual over-ride
  • make duck noises

The only things that got implemented out of these were the first two.  I put a lot of effort into making the only touchscreen we had (a SainSmart 2.8 inch TFT LCD) work as a UI for telemetry and control but was no able to make it work.  I still cannot even after buying the adapter for the arduino mega.  Usually I’m able to get around shitty documentation by digging through the code of the libraries, but this one beat me.

The large number of sensors… We had a BH1750 Digital Light Sensor, never got it to work.  We had a BMP180 Barometric Pressure/Temperature/Altitude Sensor, didn’t have time to make it work.  An HC-SR04 ultrasonic sensor, also never made it do anything.  A DHT22, the simplest sensor we could have installed… nope.  So, we had absolutely no sensor readings but it had a bunch of stuff plugged in so it looked impressive.

The actual navigation was done using a LIDAR Lite, which is a really cool i2c lidar sensor.  This was borrowed from a FIRST team and unfortunately no longer resides with the robot.  I thought about getting another one, but it’s just too expensive to put on a robot that I actually have no use for.  The chassis was the absolute biggest, most expensive RC car that walmart had to offer.. a New Bright 1:10 Radio Control Ford Raptor Truck, Black/Green.  No, I wasn’t much impressed either.  For a chassis this had loose ball joints, an anemic little lithium battery pack, and a motor that was rather poorly geared for torque.  But if the chassis was perfect then we’d have no fun accounting for it later.  The motor control comes from a Sparkfun Ludus Protoshield (which I got in a ding/dent deal and is not labeled like that on mine).  The control is just two-bit direction and a pwm line for speed.

Sparkfun ProtoCAT board

Being in the IEEE lab at MTU we had the advantage of a 3d printer.  They may have taken away the band saw and drill press because someone took off the safety switches and didn’t either restore them or hide them well enough, but we could still make small plastic parts over the course of several hours.  We made a bracket to hold the LIDAR to the servo and cut up a chunk of an old computer case to affix the servo to the chassis.  Now we had a chassis with enough sensor to let it move.  You may notice this is the exact same setup as the wheelchair robot with the exception that it’s smaller and much lower power.  When you have a hammer and all that…  It should not surprise you that the same person wrote the code for that as this one.

The wireless part was going to be a whole custom interface between two esp8266s to allow for touch-screen control and telemetry… Well, when it came right down to it I flashed the wonderful ESP-Bridge firmware to an esp8266 and clamped that onto the serial port of the arduino uno.  That worked for a very short while and then stopped.  I then replaced the esp8266, put a voltage divider on the rx pin, flashed it again and then it kept working… oops.  This theoretically gave manual control and some sensor data, but in fact it just gave updates from within the code.  That meant a constant stream of almost nonsense that looked pretty impressive to the lay-person.

The duck sounds were actually going to be quacks made by a speak-n-spell I found at goodwill, but hacking that never actually happened.  That may be an upcoming post, because using one of those for a speech synth has always been a plan of mine.

The battery was the same one that came with the RC car, a 500mah 9.6v lithium pack with charger.  Really anemic for this use but it worked for the little while we needed it to.  I upgraded that with a later revision.  The bus power is provided by a little 3A adjustable dc-dc regulator from ebay that got set to 5V and had the POT glued down.  This is much better than the linear regulator that would just dump 50% of the power from our tiny battery as heat.

The code is here

The pictures are here

the hackathon post is here

Advertisements

Amazon Dash Button Re-Hack!

July 14, 2016

So, we just got through Amazon Prime Day.  That means I got myself a handful of amazon dash buttons for $0.99 each.  How could I resist, I could do some coding in a new toolchain and architecture I’m not familiar with… ok no.  All I intended to do with these was to use the software-only hack that allows you to capture the button presses and pipe that through to my mqtt server.  Amazon decided to thwart me just a bit.

As noted in this wonderful teardown there’s a new kid on the block for amazon dash buttons (JK29LP).  The old one (JK76PL) had an stm32, the new one is an atmel.  I would normally like this, but no one else has blazed a trail for me yet so I’ll fall back to being lazy.  The new amazon dash buttons no longer broadcast the message that the original hack picks up, but I tweaked it and am back in business (although from the blink pattern you can tell what revision button you have).  There’s a new program someone wrote to have a windows daemon that runs a program or script whenever a button is pushed, and it even works on the new buttons.  But it only works on windows and I just won’t have that.

I based my work on the original code from a number of different places used for different purposes.  My modification is using the dash button to send out a message on a given topic on a given mqtt server (in my case it’s localhost).  I thought that if the windows program used the ip addresses that the router gave leases to then I could get the MAC addresses by checking the leases in my router.  If I had only been smart enough to remove this line:

if pkt[ARP].psrc == ‘0.0.0.0’: # ARP Probe

I would have seen the buttons being pressed.

from scapy.all import *
import os

def arp_display(pkt):
if pkt[ARP].op == 1: #who-has (request)
if pkt[ARP].hwsrc == ‘f0:27:2d:ef:a8:a2′: # ARP Probe
print “ARP Probe from: snuggle 1 ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m snuggle1”)
if pkt[ARP].hwsrc == ’74:75:48:6f:3b:b7′: # ARP Probe
print “ARP Probe from: snuggle 2 ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m snuggle2”)
if pkt[ARP].hwsrc == ’44:65:0d:78:94:12′: # ARP Probe
print “ARP Probe from: glad 1 ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m glad1”)
if pkt[ARP].hwsrc == ’44:65:0d:c6:e5:21’: # ARP Probe
print “ARP Probe from: glad 2 ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m glad2”)
if pkt[ARP].hwsrc == ‘0c:47:c9:7c:55:20’: # ARP Probe
print “ARP Probe from: redbull 1 ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m redbull1”)
if pkt[ARP].hwsrc == ‘0c:47:c9:ed:9c:46′: # ARP Probe
print “ARP Probe from: redbull 2 ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m redbull2”)
if pkt[ARP].hwsrc == ’44:65:0d:4d:a6:0b’: # ARP Probe
print “ARP Probe from: burt’s bees ” + pkt[ARP].psrc
os.system(“mosquitto_pub -h localhost -t displayTopic -m bees1″)

print sniff(prn=arp_display, filter=”arp”, store=0, count=0)

My code hardcodes the MAC addresses without checking for a 0.0.0.0 arp packet and prints out to the terminal and sends a mqtt command.  I run all this on my mqtt server pi and have it autostart just like the screen script.  Except not just like that because I want this one to run as root since I can’t do permissions worth a damn.  That would be here, and I’m still working on it.

I know this isn’t original as I see references to people removing that line, but I see no reference to the new dash button and this rock solid implementation of the year old python code.  That being said I also haven’t seen this tied to mqtt either, so that may be original.  NOTE: this triggers a notification on the amazon shopping app everytime a button that does not order a product is pressed.  This could be mitigated by blocking access to amazon’s servers for those buttons but I just sign out of the amazon shopping app (I could set them back up using a fake account, but it’s late).  I also like the thought of finding out how to respond to the buttons so they blink green when amazon responds, but I’m not willing to dig into that right now.

LED Cylon Painting

July 1, 2016

I was inspired by a large bag of red ‘tombstone’ LEDs to make a larson scanner.  These LEDs are exactly 0.1″ across which makes them perfect to stack side by side on perf board to get an arbitrarily long bargraph (but in a nicer form factor).  This was to be put in a box that originally held the receiver for an aftermarket wireless video game controller.  I designed and actually fitted the circuitry in there first.  The plan was to make that take serial commands (or serial over USB) and have it be an ROS peripheral  for an AI, or natural language processor, or just a roomba with some attitude.  Following this build I was watching an episode of Futurama called ‘The Honking’ which featured this distinguished gentleman:

The quote goes:

BENDER
That painting, the eyes are watching me!

[Farnsworth walks towards a portrait of Commodore LXIV.]

FARNSWORTH
Hmm. It has motor eye sensors attached to motion detectors.

BENDER
So does my butt, but I don’t frame it and put it on the wall! Although…

Then he got an idea. An awful idea. The Grinch got a wonderful, Awful idea… What if one of those paintings could follow you and was of a regal looking Cylon.  I commissioned a friend at i3detroit to mane the painting (a replica of a famous painting, but with a modified head/helmet) and got to work on getting someone else to help with the motion detection.  Yeah, I farmed out the work but that’s because I like it here in embeded-land.

The openCV code to detect faces wasn’t hard, it came from a certain mustache example some of you may be familiar with.  The painting turned out beautifully, and went to makerfaire.  The problem was that the raspberry pi is not the fastest computer for video processing and the pi camera does not deal with changes in exposure well.  The face tracking was a failure, but I hope to try that again in the future with a more powerful board.

The hardware is just shift registers, LEDs, and a pro mini.  The code is simple, but it’s a good thing I put in an auto-scanner routine for operation without a computer.  The manual control can set any LED position on, and then resume scrolling on command.

The code is here.

The pictures are here.

OpenAccess eeprom functions

July 1, 2016

I defined a lot of commands about how to store and retrieve stuff from the onboard eeprom.  Here they are with hopefully enough explanation.

//EEPROM stuff
//if I have time I should figure out why it errors out reading over 512 entries (should get 2048)
int entry_size = 64;
byte hash[32];
//store card id (currently a long, might not need to be)
char name[31];
byte priv;
byte command[64]; //add time zone

Initialization stuff, and questions about my sanity.  I defined 64 bytes of ‘command’ data, but never got around to using it (I could have made the define byte command[entry_size]).  The length is because that’s the same amount of space I use for a given user (and I can just index +1 to get that index user) .  You can see that the hash is 32 bits, and I have one priv byte which leaves an arbitrary 31 bytes to store the human readable name while keeping a nice round 64 byte per entry number.  The intent was to use this space for nonvolatile things (which could be stored in the 2560’s eeprom, but I decided to make it so the micro-controller board is fungible.  The only part of that intent that remains is the comment that references a time zone.  It would be easy to add the ability to peek and poke that byte (er… signed whatever-the-hell it would have to be to be a time zone) and have the date retrieval function use that offset.

// dumps the command area of the EEPROM
void SCmd_read_CMD()//debug, dumps command memory
{
read_EE_command();//updates working memory from EEPROM
for (int i = 0; i < 64; i++)
{
Serial.print(command[i], HEX);
}
Serial.println();
}

The command area of the eeprom stores nonvolatile things, but I never defined what.  This function reads it all out into working memory and prints it out for the benefit of the person or script that requested it (this function is triggered by a serial command).

void read_EE_command()//populate working memory from EEPROM
{
//for(int i = 0;i<64;i++)
//{
// command[i] = 0;
//}
for(int i = 0;i<64;i++)
{
command[i] = EEPROM1024.read(i);
}
}

This is the function that actually reads the command section of the eeprom and puts it into working memory.  There is a commented out section that blanks the working memory first, I don’t think that needs to still be there.

void write_EE_cmd()//populate EEPROM from working memory
{
for(int i = 0;i<64;i++)
{
EEPROM1024.write(i,command[i]);
}
}

This writes the volatile command memory to long term storage, doing the opposite of the above function.

void blank_EE_cmd()//debug

{
for(int i = 0;i<64;i++)
{
command[i] = 0;
EEPROM1024.write(i,0);
}
}

When testing code I end up filling the eeprom and need to blank parts of it to test other code, this function probably need not live in a running system.  I would recommend putting in a bunch of debug stuff, but marking it as such so it can be removed later.

void read_EE_name(int index)
{
for(int i = 0;i<31;i++)
{
name[i] = 0;
}
for (int i = 0; i < 31; i++)
{
name[i] = EEPROM1024.read(i+((index+1) * entry_size));//keeps command byte unwritten
}
return;
}

This code reads the name at a given index to working memory.  This is a common part of this code, I move stuff to and from working memory in order to check on it, change it, and then put it back.  This keeps reads and writes to the memory to a minimum and there’s less of a chance that the memory will be saved in an undesirable state.

void read_EE_hash(int index)
{
//for(int i = 0;i<64;i++)
//{
// EEPROM1024.write(i,command[i]);
//}
index +=1;//keeps command byte unwritten
int start_address = (index) * entry_size;
int end_address = start_address + entry_size;

for (int i = 0; i < 32; i++)
{
hash[i] = EEPROM1024.read(((start_address) + 32)+i);
}
return;
}

This code reads a hash at a given index to working memory.  This code is framed a bit differently than the function above, but it accomplishes the same task.  This looks older and more complicated, and should probably be condensed to look like the name retrieval function.

void dump_EE(int start_address, int end_address)//debug
{
//byte data[64];
for (int address = start_address; address < end_address; address++)
{
char data;
data = EEPROM1024.read(address);
Serial.print(address);
Serial.print(” :”);
Serial.print(data, HEX);
Serial.print(” :”);
Serial.println(data);
}
//Serial.println(“DONE”);
return;
}

This is another debug statement to dump eeprom data.  I mostly store my data as ascii so it prints the hex and ascii.  This is how I checked up on the memory during development, and if the intent is to run this board as a dumb I/O terminal it could be useful to leave in or as a method of backing up all the raw data.  If there is a problem this could be helped in debugging it since it does not try to be smart and just gives you all the addresses you ask for.  As always there’s some cruft left from previous formatting of the data, I’d remove the first one but leave the second one as it would be nice to be able to uncomment that later if something starts locking up.

void serial_user_print(int index)//debug? if in final make serial prints logging
{
read_EE_name(index);
read_EE_priv(index);
read_EE_hash(index);
Serial.print(index,DEC);
Serial.print(“;”);
for (int j = 0; j < (sizeof(name)/sizeof(name[0])); j++)
{
if(name[j] != 0)
Serial.print(name[j]);
}
Serial.print(“;”);
Serial.print(priv,HEX);
Serial.print(“;”);
for (int j = 0; j < (sizeof(hash)/sizeof(hash[0])); j++)
{
Serial.print((char)hash[j]);
}
Serial.println();
}

This prints out all the information about a user at a given address.  A more verbose version could be written to decode the priv bits as well.  This is a smarter version of the dump function which gives one index’s user data in a concise format.  It is only sent to the serial port, not to the printer (as pointed out by the comment).

// removes the user with <hash>
//format: RMV_USRH;<hash>
void SCmd_remove_user_hash()
{
char *arg;
arg = SCmd.next();
remove_user_hash(arg);
Serial.println(“user removed”);
}
void remove_user_hash(char hashd[32])////////////////////////////////////////////////////////////////////////////////////////////////untested
{
for(int i = 0;i<num_users;i++)
{
boolean succeed = 1;
read_EE_hash(i);
for (int j = 0; j < (sizeof(hash)/sizeof(hash[0])); j++)
{
if(hash[j] != hashd[j])
{
succeed == 0;
j+=((sizeof(hash)/sizeof(hash[0])));//break
}
}
if(succeed)
{
remove_user(i);
Serial.println(“user removed”);
i+=num_users;//break
}
}
}

This is supposed to be a serial wrapper and the associated function to remove a user with a given hash.  It would search out a perfect match for a given hash then blow away the first user with that hash.  It looks like it actually prints “user removed” twice, once from each function which should be corrected (remove the first instance, leave the one in the internal function and make it log that to the printer as well as the serial port).  There is no output if the hash isn’t found (this should be corrected).  Notice how I break the loop? Classic me. Use at your own peril.

// removes the user with <name>
//format: RMV_USRN;<name>
void SCmd_remove_user_name()
{
char *arg;
arg = SCmd.next();
remove_user_name(arg);
Serial.println(“user removed”);
}
void remove_user_name(char named[31])///////////////////////////////////////////////////////////////////////////////////////////////untested
{
for(int i = 0;i<num_users;i++)
{
boolean succeed = 1;
read_EE_name(i);
for (int j = 0; j < (sizeof(name)/sizeof(name[0])); j++)
{
if((name[j] != 0 || named[j] != 0) && name[j] != named[j])
{
succeed == 0;
j+=((sizeof(name)/sizeof(name[0])));//break
}
}
if(succeed)
{
remove_user(i);
Serial.println(“user removed”);
i+=num_users;//break
}
}
}

This is supposed to be a serial wrapper and the associated function to remove a user with a given human readable name.  It would search out a perfect match for a given name, then blow away the first user with that hash.  It looks like it actually prints “user removed” twice, once from each function which should be corrected (remove the first instance, leave the one in the internal function and make it log that to the printer as well as the serial port).  There is no output if the hash isn’t found (this should be corrected).  Notice how I break the loop? Classic me. Use at your own peril.

// removes the user at <index>
//format: RMV_USR;<index>
void SCmd_remove_user()
{
char *arg;
arg = SCmd.next();
remove_user(atoi(arg));
Serial.println(“user removed”);
}
void remove_user(int i)
{
logging(logDate());
logging(” – “);
loggingln(“removing user”);
serial_user_print(i);
String name = “Free”;
byte priv= 0;
//gonna need to add a blank cardid ince that gets implemented
String hash = “00000000000000000000000000000000”;
write_EE(i, name, priv, hash);
logging(logDate());
logging(” – “);
loggingln(“user removed”);
}

This will remove a user at a given index, log it properly, blanking the hash, priv byte, and changing the name to ‘Free’.  This is the way I think it should be done, finding the user you want to delete by reading through the entries and deciding to eliminate it by index, the above functions were an attempt to automate that (which are a bit unfinished).  This sorta creates a sacred cow with that name, but I’m not worried about it.  I could also use “-1” but “Free” reads better when you dump the data.  Also, there’s no spellchecker in my IDE, so that comment typo was only found here in wordpress.

void populate_blank_users()//debug
{
for (int i = 0;i<num_users;i++)
remove_user(i);
}

Want to remove everyone? Probably do if you’re debugging a bunch.  This just loops through everyone and does that.

// adds a user at a given index
//format: ADD_USR;<index>;<name>;<priv byte>;<hash>
void SCmd_add_user()//rename to SCmd_add_user_index() once above function is done
{
char *arg;
arg = SCmd.next();
int index = atoi(arg);
arg = SCmd.next();
String name = arg;
arg = SCmd.next();
byte priv= atoi(arg);
arg = SCmd.next();
String hash = arg;
write_EE(index, name, priv, hash);
}

This adds a user to a given index, be warned you need the desired name, precalculated hash and priv byte to use this.  It’s mostly useful for restoring after a backup, although I could create a standalone tool to calculate those based on the card ID, PIN, and asking what privileges the user should have.  I already have those functions, they just need to be tweaked to reply to some user input.

//create a function like SCmd_add_user that adds a user at the first free index

I could have sworn I did this but I guess it’s left as an exercise for the reader.  It’s like the ‘search and destroy’ commands, but it’s a ‘search and implant’ which uses the functionality already in the user add functionality if you use they keypad to add the user.

// reads the user at <index>
//format: READ_USR;<index>
void SCmd_read_user()
{
char *arg;
arg = SCmd.next();
serial_user_print(atoi(arg));
//Serial.println(“DONE”);
}

This reads a user at a given index, this is a serial wrapper for a function already made modular for other reasons.

// dumps all users present
//format: DUMP_USR
void SCmd_dump_users()
{
for (int i=0;i<num_users;i++)
{
if(user_present(i))
{
Serial.println(i);
serial_user_print(i);
}
}
//Serial.println(“DONE”);
}

This dumps all present users (use the raw read if you want all the data).  This uses the user_present function, I thought that used the name being ‘Free’ but I just checked and it uses priv byte not equal to zero.  Whatever gets the job done, this actually returns whether the user can open the door and that effectively limits the other functions because I use the door open timer to allow for the triggering of the other functions.

void dump_all_EE() //debug stuff
{
for (int address = 0; address < 131072; address++)
{
char data;
data = EEPROM1024.read(address);
Serial.print(address);
Serial.print(” :”);
Serial.println(data, HEX);
Serial.print(address);
Serial.print(” :”);
Serial.println((char)data);
//Serial.print(” :”);
//Serial.println(data);
}
//Serial.println(“DONE”);
}

Dump all the eeprom!  Hardcoded endstop, don’t remember why but there it is.

void write_EE(int index, String name, byte priv, String hash)
{
index +=1;
char data[64] = {
};
for(int i = 0;i<31;i++)
{
if(name[i] == 0)
{
i+=31;//break
}
else
{
data[i] = name[i];
}
}
data[31] = priv;
for(int i = 0;i<32;i++)
{
data[i+32] = hash[i];
}

int start_address = (index) * entry_size;
int end_address = start_address + entry_size;

//Serial.println(“Writing data”);
for (int address = start_address; address < end_address; address++)
{
EEPROM1024.write(address, data[address % entry_size]);
}
//Serial.println();
}

Here is how I write the eeprom for a user index.  This is used when I dump working memory back to a user slot.  I have the serial wrapper for it, or it is called after adding a user.

This may seem like a lot, and you may not need much of it but I think it’s nice to have.  This is part of the OpenAccess project.

 

OpenAccess Exit button/relay

July 1, 2016

This has to do with the exit button/relay.  I call these functions from elsewhere, but basically it sets the timeout for activating the exit relay.  I also use this as a timeout to enter commands to the system (enter or leave modes, like adding users)

//exit button stuff
const int exit_button = 48; //digital pin the exit button is attached to
const int exit_relay = 0; //relay the door solenoid is on
const unsigned long exit_time = 3000; //time to keep the door open in thousandths of a second
boolean exiting = 0; //whether or not the door should be open (and timer running)
unsigned long exit_start_time = 0; //used for count down timer

That is the initialization; sets the button, relay, timeout, and two variables so it can let the rest of the code execute while it’s open.

void exit()
{
logging(logDate());
logging(” – “);
loggingln(“exit relay open”);
exiting = 1;
exit_start_time = current_time;
digitalWrite(relaysToPins[exit_relay], HIGH);
}

That starts the ‘exiting’ procedure, starts the timer rolling and opens the door.

void exit_timeout()
{
logging(logDate());
logging(” – “);
loggingln(“exit relay closed”);
LCD_default();
RFID_reset();
exiting = 0; //no longer in exiting mode
//exit_start_time = 0; //not needed
digitalWrite(relaysToPins[exit_relay], LOW); //close door
}

That stops the exit procedure, pretty simple really.

if (!digitalRead(exit_button)) //exit button pressed
{
//Serial.println(“exiting”);
exit();
}
//Serial.println(“past exiting”);

That checks to see if the exit button was pressed, and starts the exit procedure.

if (exiting)
{
if (current_time – exit_start_time > exit_time) //exit time out loop
{
exit_timeout();
}
}

That checks to see if the door should be re-locked (you can of course extend the timer with more code that backs up the start time to cheat this check)

That’s all there is to the exit button and exit relay code.  It can also be called like any other relay from the I/O section, but it operates on its own unlike the others.  This is part of the OpenAccess project.

OpenAccess Keypad functions

July 1, 2016

char keypad_matrix()

{
//Serial.println(“keypad_matrix”);
if(Serial3.available())
{
char result = Serial3.read();//Read key press
return result;
}
else
{
return ‘ ‘;//equivalent to -1, a null result
}
//delay(10);
}

This code polls for keypresses.  If it returns anything then the keypresses are acted on.  Stuff like this is why it is so important to keep the code looping and not blocking.  If there are multiple keypresses buffered this will grab one and leave the rest for next loop.

void enter_pin(char c)
{
//Serial.print(“Pin Button = “);
//Serial.println(reader2);
LCD_disp(“*”);
pin[digit] = c – ‘0’;//stores as numbers rather than characters
digit++;
}

Here is how a pin is incremented, it is one of the few places I do not overwrite the entire LCD line at once.

void backspace_pin()
{
pin[digit] = 15;
//Serial.println(“backspace”);
LCD_displn(” “,1);
LCD_displn(“Pin:”,1);
if(digit >0)
{
digit–;
pin[digit] = 15;
}
for (int i = 0;i<digit;i++)//repopulates asterisks
{
LCD_disp(“*”);
}
//for(int i=0;i<(sizeof(pin)/sizeof(pin[0]));i++)
//{
// Serial.print(pin[i]);
//}
}

Removing a digit is tricky, I have to set it to an impossible to type character and rewrite the line of asterisks because I don’t support arbitrary cursor positioning.

 

void clear_pin()
{
digit = 0;
for(int i=0;i<(sizeof(pin)/sizeof(pin[0]));i++)
{
pin[i] = 15;
}
}

Clearing a pin is easy, set back to unprintable.  These steps are done in the key taking process which is longer and in the main logic code.  This is part of the greater OpenAccess project.

OpenAccess LCD functions

July 1, 2016

Here is the set of variables related to the LCD, the backlight and contrast values are important.  ‘printname’ is not here for the LCD, it actually is used when returning strings over the serial port.  ‘transmit’ is what I use to send commands to the LCD.

//LCD stuff

int back_val = 255;
int cont_val = 90;
String transmit = “”;
String printname = “”;

This is my initializer for the LCD (the LCD is on Serial3), it sets the contrast and backlight value to the values in memory (could use the command bytes) so that whatever state the LCD is in, it gets set up right.

void LCD_init()
{
Serial3.begin(115200);
LCD_clear();

transmit = “CONTRAST;”;
transmit += cont_val;
Serial3.println(transmit);

transmit = “BACKLIGHT;”;
transmit += back_val;
Serial3.println(transmit);
}

These are the commands to display things on the LCD manually (like using the Serial print and println commands).  These are used internal to the code all the time, there are wrappers though that let them get used by the serial port commands.

void LCD_displn(String prints,int line)//displays on a given line, does not emulate CR LF
{
transmit = “DISPLN;”;
transmit += line;
transmit += “;”;
transmit += prints;
Serial3.println(transmit);
}
void LCD_disp(String prints)
{
transmit = “DISP;”;
transmit += prints;
Serial3.println(transmit);
}

These set the backlight and contrast from the serial console on the OpenAccess

// sets contrast
//format: CONTRAST;<analog value, 0-255 or nothing for default>
void SCmd_contrast()
{

transmit = “CONTRAST;”;
char *arg;
//Serial.println(“contrast”);
arg = SCmd.next();
if (arg != NULL)
{
transmit += arg;
}
else
{
transmit += cont_val;
}
Serial3.println(transmit);
}
// sets backlight
//format: BACKLIGHT;<analog value, 0-255 or nothing for default>
void SCmd_backlight()
{

transmit = “BACKLIGHT;”;
char *arg;
//Serial.println(“backlight”);
arg = SCmd.next();
if (arg != NULL)
{
transmit += arg;
}
else
{
transmit += back_val;
}
Serial3.println(transmit);
}

This is my clear command (it is a 2 line LCD)  This is modular so you can fill in your own code to clear whatever display you use:

inline void LCD_clear()
{
LCD_displn(” “,0);
LCD_displn(” “,1);
}

This is how you can manually overwrite the display from the console:

// uses display
//format: DISP;<0=line 1, 1=line 2 (or no argument to clear the display)>;<string (or no argument to clear the line)>
void SCmd_display()
{
char *arg;
arg = SCmd.next();
if (arg != NULL)
{
if (atoi(arg) == 0)
{
arg = SCmd.next();
if (arg != NULL)
{
LCD_displn(arg,0);
}
else
{
LCD_displn(” “,0);
}
}
else if (atoi(arg) == 1)
{
arg = SCmd.next();
if (arg != NULL)
{
LCD_displn(arg,1);
}
else
{
LCD_displn(” “,1);
}
}
}
else
{
LCD_clear();
}
}

 

This runs every loop that nothing interesting is happening and chooses the splash screen:

void LCD_default()
{
LCD_clear();
if(doorbell)
{
LCD_displn(“Welcome: press “,0);
LCD_displn(“\”#\” to enter “,1);
}
else if(doorhold)
{
LCD_displn(“Door held: press”,0);
LCD_displn(“\”#\” to enter “,1);
}
else
{
LCD_displn(“i3 Detroit “,0);
LCD_displn(“Hackerspace “,1);
}
}

That’s my LCD functions, you can see that I compartmentalized it a lot so it is easy to change the protocol or library of your display. This is part of the greater OpenAccess project.

OpenAccess RFID functions

July 1, 2016

Here are the wiegand readers:

(my other comments on wiegand can be found here)

//RFID reader stuff
#include <WIEGAND26.h> // Wiegand 26 reader format library
byte reader1Pins[]={
19,18}; // Reader 1 connected to pins 19,18
byte reader2Pins[]={
2,3}; // Reader2 connected to pins 2,3
long reader1 = 0;
int reader1Count = 0;
long reader2 = 0;
int reader2Count = 0;
const byte reader2buzz = 6; //pin for the buzzer on Reader2
const byte reader2led = 4; //pin for led1 on Reader2
const byte reader2led2 = 5; //pin for led2 on Reader2
const byte reader1buzz = 7; //pin for the buzzer on Reader1
const byte reader1led = 8; //pin for led1 on Reader1
const byte reader1led2 = 17; //pin for led2 on Reader1
WIEGAND26 wiegand26;
//Get the interrupt number of a given digital pin on an Arduino MEGA.
const byte pinToInterrupt[22] = {
-1,-1,0,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5,4,3,2};

This declares all the pins used , it gives a map from pin number to interrupt numbers (I got that from Mike and I like the idea), and the global variables checked by the wiegand functions.  The pins used for the RFID stuff are defined in a function:

void RFID_init()
{
pinMode(reader2buzz, OUTPUT);
pinMode(reader2led, OUTPUT);
pinMode(reader2led2, OUTPUT);
digitalWrite(reader2led, 1); //active low
digitalWrite(reader2led2, 1); //active low
digitalWrite(reader2buzz, 1); //active low
attachInterrupt(pinToInterrupt[reader2Pins[1]], callReader2One, CHANGE);
attachInterrupt(pinToInterrupt[reader2Pins[0]], callReader2Zero, CHANGE);
wiegand26.initReaderTwo();
pinMode(reader1buzz, OUTPUT);
pinMode(reader1led, OUTPUT);
pinMode(reader1led2, OUTPUT);
digitalWrite(reader1led, 1); //active low
digitalWrite(reader1led2, 1); //active low
digitalWrite(reader1buzz, 1); //active low
attachInterrupt(pinToInterrupt[reader1Pins[1]], callReader1One, CHANGE);
attachInterrupt(pinToInterrupt[reader1Pins[0]], callReader1Zero, CHANGE);
wiegand26.initReaderOne();
}

I broke up all the init functions and stuff like that because as I built this code I changed hardware so often that knowing where to edit the code is very valuable.  The RFID readers have timeouts for when to give up after they’ve accepted a card:

//RFID timer stuff
const unsigned long RFID_time = 30000; //timeout for the RFID scan in thousandths of a second
unsigned long RFID_start_time = 0; //used for the count down timer

These two functions that reset some global variables in the case an rfid entry attempt is not successful (garbage collection).

void RFID_reset()
{
clear_pin();
//RFID_start_time = 0; //not needed
tag_read = 0;
digit = 0;
new_tag_entry = 0;
}
void RFID_error_out()
{
can_add = 0; //reset user capabilities
can_doorhold = 0; //reset user capabilities
can_doorbell = 0; //reset user capabilities
RFID_reset();
beep_repeat(2,200,3);//delay timer
LCD_default();
}

The readers can beep, that function is defined here (0 beeps both readers if you didn’t figure it out):

void beep_reader(int reader,int d)//blocking
{
switch(reader){
case 0:
{
digitalWrite(reader2led, 0);
digitalWrite(reader2buzz, 0);
digitalWrite(reader1led, 0);
digitalWrite(reader1buzz, 0);
delay(d);
digitalWrite(reader2led, 1);
digitalWrite(reader2buzz, 1);
digitalWrite(reader1led, 1);
digitalWrite(reader1buzz, 1);
}
case 1:
{
digitalWrite(reader1led, 0);
digitalWrite(reader1buzz, 0);
delay(d);
digitalWrite(reader1led, 1);
digitalWrite(reader1buzz, 1);
}
case 2:
{
digitalWrite(reader2led, 0);
digitalWrite(reader2buzz, 0);
delay(d);
digitalWrite(reader2led, 1);
digitalWrite(reader2buzz, 1);
}
default:
{
}
}
}

That beep code is yet again wrapped up here to add a duration for repeated beeps:

void beep_repeat(int reader,int time,int number)//blocking
{
/*Serial.print(“beep repeat”);
Serial.print(” “);
Serial.print(reader);
Serial.print(” “);
Serial.print(time);
Serial.print(” “);
Serial.println(number);*/
for(int i = 0;i<number;i++)
{
beep_reader(reader,time);
delay(time);
}
}

This is how I call it from the serial port:

// beeps reader<reader number> for <duration> in delay(arg) format
//format: BEEP;<reader number>;<duration>
void SCmd_beep()
{
Serial.println(“beepcommand”);
char *arg;
arg = SCmd.next();
if (arg != NULL)
{
if(atoi(arg) == 1)
{
arg = SCmd.next();
if (arg != NULL)
{
beep_reader(1,atoi(arg));
}
}
else if(atoi(arg) == 2)
{
arg = SCmd.next();
if (arg != NULL)
{
beep_reader(2,atoi(arg));
}
}
else if(atoi(arg) == 0)
{
arg = SCmd.next();
if (arg != NULL)
{
beep_reader(0,atoi(arg));
}
}
}
}

This stuff is at the bottom of the code, it is needed because the wiegand26 library is not written as well as it could be:

void callReader1Zero(){
wiegand26.reader1Zero();
}
void callReader1One(){
wiegand26.reader1One();
}
void callReader2Zero(){
wiegand26.reader2Zero();
}
void callReader2One(){
wiegand26.reader2One();
}

This is part of the greater OpenAccess project.

OpenAccess RTC functions

July 1, 2016

These declares are for the i2c eeprom and realtime clock:

//I2C stuff
#include <Wire.h> // Needed for I2C Connection to the DS1307 date/time chip
#include <DS1307.h> // DS1307 RTC Clock/Date/Time chip library
#include <E24C1024.h> // Needed for the onboard EEPROM
uint8_t second, minute, hour, dayOfWeek, dayOfMonth, month, year; // Global RTC clock variables. Can be set using DS1307.getDate function.
DS1307 ; // RTC Instance

Those are used in their own functions, this one takes commands from the serial and does RTC things with them:

// uses RTC
//format: RTC;<0=write, 1=read>;<second, 0-59 (write only)>;<minute,0-59 (write only)>;<hour, 1-23 (write only)>;<dayOfWeek, 1-7 (write only)>;<dayOfMonth, 1-28/29/30/31 (write only)>;<month, 1-12 (write only)>;<year, 0-99 (write only)>
void SCmd_rtc()
{
char *arg;
//Serial.println(“rtc”);
arg = SCmd.next();
if (arg != NULL)
{
if(atoi(arg))
{
//Serial.println(“read rtc”);
//logDate();
Serial.println(logDate());//should be logging?
}
else
{
//Serial.println(“write rtc”);
arg = SCmd.next();
if (arg != NULL)
{
second=atoi(arg);
}
else
{
//Serial.println(“No second argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
minute=atoi(arg);
}
else
{
//Serial.println(“No minute argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
hour=atoi(arg);
}
else
{
//Serial.println(“No hour argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
dayOfWeek=atoi(arg);
}
else
{
//Serial.println(“No dow argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
dayOfMonth=atoi(arg);
}
else
{
//Serial.println(“No dom argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
month=atoi(arg);
}
else
{
//Serial.println(“No month argument”);
}
arg = SCmd.next();
if (arg != NULL)
{
year=atoi(arg);
}
else
{
//Serial.println(“No year argument”);
}
ds1307.setDateDs1307(second,minute,hour,dayOfWeek,dayOfMonth,month,year);
/* Sets the date/time (needed once at commissioning)
uint8_t second // 0-59
uint8_t minute // 0-59
uint8_t hour // 1-23
uint8_t dayOfWeek // 1-7
uint8_t dayOfMonth // 1-28/29/30/31
uint8_t month // 1-12
uint8_t year // 0-99
*/
}
}
else
{
//Serial.println(“No arguments”);
}
}

This next one returns the date in a print/logable form and is used all over the code to timestamp things:

String logDate()
{
String date;
ds1307.getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
date+=”20″;//hack
date+=year;
date+=”-“;
if(month < 10)
{
date+=”0″;
}
date+=month;
date+=”-“;
if(dayOfMonth < 10)
{
date+=”0″;
}
date+=dayOfMonth;
date+=’T’;
if(hour < 10)
{
date+=”0″;
}
date+=hour;
date+=”:”;
if(minute < 10)
{
date+=”0″;
}
date+=minute;
date+=”:”;
if(second < 10)
{
date+=”0″;
}
date+=second;
date+=” “;
switch(dayOfWeek){
case 1:
{
date+=”SUN”;
break;
}
case 2:
{
date+=”MON”;
break;
}
case 3:
{
date+=”TUE”;
break;
}
case 4:
{
date+=”WED”;
break;
}
case 5:
{
date+=”THU”;
break;
}
case 6:
{
date+=”FRI”;
break;
}
case 7:
{
date+=”SAT”;
break;
}
}
return date;
}

That’s it, that’s what the RTC is for: timestamps on things. This is part of the greater OpenAccess project.

OpenAccess temperature functions

July 1, 2016

The thought behind the temperature sensor was to be able to set the contrast of the LCD based on it, I didn’t know how to put the logic in the uart lcd so I made it externally controllable with the thought to add logic later.  The function to check the temperature can be called by the serial port and reports back that way and to the dot matrix printer.  Here is the temperature sensor setup for checking outside temperature:

// Interface to DS1822 chip, record data to EEPROM
#include <OneWire.h>
OneWire ds(30); // define the pin the interface is on */
float current_temperature;

Here is my function for getting the temperature:

float getTemp()
{
byte data[9];
byte addr[8];
int temp;

while ( !ds.search(addr))
{
//Serial.print(“No more addresses.\n”);
ds.reset_search();
}
ds.reset();
ds.select(addr);
ds.write(0x44,1);
delay(600);
byte present = ds.reset();
ds.select(addr);
ds.write(0xBE);
for (byte i = 0; i < 9; i++)
{
data[i] = ds.read();
}
boolean negative = (data[1] & 1<<5);
data[1] <<=4;
data[1] >>=4;
temp = data[1];
temp <<= 8; /* shift left eight bits */
temp += data[0]; /* add second byte of temperature */
float tempFloat = (float)temp/16.0;
if(negative)
{
tempFloat = -tempFloat;
}
return tempFloat;
}

This temperature is used in the function another wrapper function to get the temperature and log it:

// logs temperature
void check_temp()
{
char temp[8];
current_temperature = getTemp();//stored for calculation of contrast value later
ftoa(temp,current_temperature,2);
//dtostrf(voltage,8,2,volt); should work?
printname = “Temperature outside: “;
printname += temp;

logging(logDate());
logging(” – “);
loggingln(printname);
}

That variable “current_temperature” is used nowhere else and need not be declared globally.  This is part of the greater OpenAccess project.