Skip to content

Instantly share code, notes, and snippets.

@movitto

movitto/barber.ino

Last active Sep 26, 2015
Embed
What would you like to do?
Bitcoin Aware Barber's Pole
//// Bitcoin Barber Shop Pole
//// Author: Mo Morsi <mo@morsi.org>
//// Arduino Controller Sketch
////
//// License: MIT
//// For use at the Syracuse Innovators Guild (sig315.org)
#include <SPI.h>
#include <Ethernet.h>
//////////////////////////////////
//// sketch parameters
byte mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
int port = 80;
char server[] = "projects.morsi.org";
char host[] = "Host: projects.morsi.org";
char request[] = "GET /barber/ HTTP/1.1";
char user_agent[] = "User-Agent: arduino-ethernet";
char close_connection[] = "Connection: close";
char content_length_header[] = "Content-Length";
char CR = '\r';
char NL = '\n';
unsigned long lastConnectionTime = 0;
const unsigned long postingInterval = 300000; // - every 5 mins
boolean lastConnected = false;
const int max_data = 32;
int data_buffer_pos = 0;
char data_buffer[max_data];
int content_length = -1;
boolean in_body = false;
int current_btc = 0;
int current_btc_decimal = 0; // since were not using floats
const int blue_pin = 5;
const int red_pin = 7;
unsigned long lastLightingTime = -1;
const unsigned long lightingInterval = 5000;
//////////////////////////////////
// arduino hook in points & config
EthernetClient client;
void setup() {
pins_config();
serial_config();
delay(5000);
net_config();
delay(1000);
turn_on_both();
Serial.println("started");
}
void loop() {
net();
lights();
}
void pins_config(){
pinMode(blue_pin, OUTPUT);
pinMode(red_pin, OUTPUT);
}
void serial_config(){
Serial.begin(9600);
while (!Serial) { ; } // this check is only needed on the Leonardo
}
/////////////////////////////
// network operations
void net(){
net_read();
if(should_reset())
net_reset();
else if(should_issue_request())
net_request();
process_response();
lastConnected = client.connected();
}
void block(){
for(;;) { ; }
}
boolean should_reset(){
return !client.connected() && lastConnected;
}
void net_reset(){
client.stop();
}
boolean should_issue_request(){
return !client.connected() && (millis() - lastConnectionTime > postingInterval);
}
void net_config(){
if (Ethernet.begin(mac) == 0) {
Serial.println("net failed");
block();
}
}
void net_read(){
if(client.available()) {
char c = client.read();
buffer_append(c);
//Serial.print(c);
}
}
void net_request(){
if (client.connect(server, port)) {
client.println(request);
client.println(host);
client.println(user_agent);
client.println(close_connection);
client.println();
lastConnectionTime = millis();
}else {
client.stop();
}
}
//////////////////////////////////
// data buffer management
void buffer_append(char c){
data_buffer[data_buffer_pos] = c;
data_buffer_pos += 1;
if(data_buffer_pos >= max_data)
data_buffer_pos = 0;
}
void buffer_reset(){
data_buffer_pos = 0;
}
// moves last char in buffer to first, sets pos after
void buffer_cycle(){
data_buffer[0] = data_buffer[data_buffer_pos-1];
data_buffer_pos = 1;
}
void buffer_print(){
Serial.print("buf ");
Serial.print(data_buffer_pos);
Serial.print(": ");
for(int p = 0; p < data_buffer_pos; p++)
Serial.print(data_buffer[p]);
Serial.println();
}
//////////////////////////////////
// http parsing / handling
// https://en.wikipedia.org/wiki/HTTP_message_body
int char_pos(char ch){
for(int p = 1; p < data_buffer_pos; p++)
if(data_buffer[p] == ch)
return p;
return -1;
}
int seperator_pos(){
return char_pos(':');
}
int decimal_pos(){
return char_pos('.');
}
boolean status_detected(){
if(data_buffer_pos < 4) return false;
int cr_pos = data_buffer_pos - 3;
int lf_pos = data_buffer_pos - 2;
int alpha_pos = data_buffer_pos - 1;
// only upper case letters
int alpha_begin = 65;
int alpha_end = 90;
return data_buffer[cr_pos] == CR &&
data_buffer[lf_pos] == NL &&
data_buffer[alpha_pos] >= alpha_begin &&
data_buffer[alpha_pos] <= alpha_end;
}
boolean header_detected(){
if(data_buffer_pos < 5) return false;
int cr_pos = data_buffer_pos - 2;
int lf_pos = data_buffer_pos - 1;
return seperator_pos() != -1 &&
data_buffer[cr_pos] == CR &&
data_buffer[lf_pos] == NL;
}
boolean is_header(char* name){
int pos = 0;
while(name[pos] != '\0'){
if(name[pos] != data_buffer[pos])
return false;
pos++;
}
return true;
}
boolean body_detected(){
if(data_buffer_pos < 4) return false;
int first_cr = data_buffer_pos - 4;
int first_lf = data_buffer_pos - 3;
int second_cr = data_buffer_pos - 2;
int second_lf = data_buffer_pos - 1;
return (data_buffer[first_cr] == CR &&
data_buffer[first_lf] == NL &&
data_buffer[second_cr] == CR &&
data_buffer[second_lf] == NL);
}
int extract_content_length(){
int value_pos = seperator_pos() + 1;
char content[data_buffer_pos - value_pos];
for(int p = value_pos; p < data_buffer_pos; p++)
content[p-value_pos] = data_buffer[p];
return atoi(content);
}
void process_headers(){
if(status_detected()){
buffer_cycle();
}
else if(header_detected()){
if(is_header(content_length_header))
content_length = extract_content_length();
buffer_reset();
}
else if(body_detected()){
in_body = true;
buffer_reset();
}
}
int extract_new_btc(){
int decimal = decimal_pos();
int buf_size = decimal == -1 ? data_buffer_pos - 1 : decimal;
int iter_end = decimal == -1 ? data_buffer_pos : decimal;
char value[buf_size];
for(int p = 0; p < iter_end; p++)
value[p] = data_buffer[p];
return atoi(value);
}
int extract_new_btc_decimal(){
int decimal = decimal_pos();
if(decimal == -1 || decimal == data_buffer_pos - 1) return 0;
int buf_size = data_buffer_pos - decimal - 1;
int iter_start = decimal + 1;
char value[buf_size];
for(int p = iter_start; p < data_buffer_pos; p++)
value[p - iter_start] = data_buffer[p];
return atoi(value);
}
void process_body(){
if(!in_body || data_buffer_pos < content_length) return;
//buffer_print();
process_new_btc(extract_new_btc(), extract_new_btc_decimal());
content_length = -1;
in_body = false;
buffer_reset();
}
void process_response(){
if(!in_body)
process_headers();
else
process_body();
}
//////////////////////////////////
// target specific data processing
void print_btc(int btc, int btc_decimal){
Serial.print(btc);
Serial.print('.');
Serial.print(btc_decimal);
Serial.println();
}
boolean value_increased(int new_btc, int new_btc_decimal){
return new_btc > current_btc || (new_btc == current_btc && new_btc_decimal > current_btc_decimal);
}
boolean value_decreased(int new_btc, int new_btc_decimal){
return new_btc < current_btc || (new_btc == current_btc && new_btc_decimal < current_btc_decimal);
}
void process_new_btc(int new_btc, int new_btc_decimal){
//print_btc(current_btc, current_btc_decimal);
//print_btc(new_btc, new_btc_decimal);
if(value_increased(new_btc, new_btc_decimal)){
turn_on_blue();
}
else if(value_decreased(new_btc, new_btc_decimal)){
turn_on_red();
}
else{
turn_on_both();
}
current_btc = new_btc;
current_btc_decimal = new_btc_decimal;
}
//////////////////////////////////
// pin output handling
boolean should_turn_off(){
return lastLightingTime != -1 && (millis() - lastLightingTime > lightingInterval);
}
void lights(){
if(should_turn_off()){
turn_off_both();
lastLightingTime = -1;
}
}
void turn_on_blue(){
lastLightingTime = millis();
digitalWrite(blue_pin, HIGH);
}
void turn_off_blue(){
digitalWrite(blue_pin, LOW);
}
void turn_on_red(){
lastLightingTime = millis();
digitalWrite(red_pin, HIGH);
}
void turn_off_red(){
digitalWrite(red_pin, LOW);
}
void turn_on_both(){
turn_on_blue();
turn_on_red();
}
void turn_off_both(){
turn_off_blue();
turn_off_red();
}
# HTTP -> HTTPS proxy
# Written to query the bitcoinaverage.com via http (only accessible by https).
# Run as a standard Rack / Sinatra application
#
# Author: Mo Morsi <mo@morsi.org>
# License: MIT
require 'sinatra'
require 'open-uri'
URL = "https://api.bitcoinaverage.com/ticker/USD/last"
get '/' do
open(URL) do |content|
content.read
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment