Bitcoin Aware Barber's Pole
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//// 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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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