Last active
August 1, 2020 03:23
-
-
Save theepicsnail/48e76f9d293c70f48d2633a383c20ba5 to your computer and use it in GitHub Desktop.
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
#include <Ethernet.h> | |
#define rfTransmitPin 4 | |
#define timeDelay 105 | |
#define BEEP 0 | |
#define VIBRATE 1 | |
#define SHOCK 2 | |
/* RF stuff */ | |
#define LONG_PULSE 1260 | |
#define SHORT_PULSE 315 | |
void sendByte(byte b) { | |
byte mask = B10000000; | |
while(mask){ | |
digitalWrite(rfTransmitPin, HIGH); | |
delayMicroseconds(b&mask?LONG_PULSE:SHORT_PULSE); | |
digitalWrite(rfTransmitPin, LOW); | |
delayMicroseconds(b&mask?SHORT_PULSE:LONG_PULSE); | |
mask>>=1; | |
} | |
} | |
void sendCommand(byte command, byte level) { | |
digitalWrite(rfTransmitPin, HIGH); | |
delayMicroseconds(8400);//8715); | |
digitalWrite(rfTransmitPin,LOW); | |
delayMicroseconds(4200);//4305); | |
sendByte(B00100101); | |
sendByte(B00000001); | |
sendByte((level<<4)|(command<<2)|1); | |
sendByte(B00001111); | |
} | |
// | |
/* Exposed collar functions */ | |
#define MAX_SHOCK_LEVEL 6 | |
#define SHOCK_COOLDOWN_MS 5000 | |
unsigned long lastTime=-SHOCK_COOLDOWN_MS; | |
bool shock(byte level) { | |
if(level > MAX_SHOCK_LEVEL) { | |
// too high | |
return false; | |
} | |
unsigned long curTime = millis(); | |
unsigned long delta = curTime - lastTime; // correctly handles unsigned rollover. | |
if(delta < SHOCK_COOLDOWN_MS) { | |
// too fast | |
return false; | |
} | |
lastTime = curTime; | |
// Shock requires a beep in front for training purposes! | |
for(int i = 0 ; i < 6 ; i ++) | |
sendCommand(BEEP, 0); | |
delay(500); | |
for(int i = 0 ; i < 6 ; i++) | |
sendCommand(SHOCK, level); | |
return true; | |
} | |
bool vibrate(byte arg) { | |
byte level = arg&0xf; | |
byte len = (arg>>4); | |
for(int i = 0 ; i < 6 ; i ++) | |
sendCommand(BEEP, 0); | |
delay(500); | |
for(int j = 0 ; j < len ; j++) { | |
for(int i = 0 ; i < 6 ; i ++) | |
sendCommand(VIBRATE, level); | |
delay(300); | |
} | |
return true; | |
} | |
typedef struct { | |
String name; | |
bool (*func)(byte); | |
byte arg; | |
byte width; | |
} Command ; | |
const Command COMMANDS[] = { | |
{"Shock 1", shock, 1, 2}, | |
{"Shock 2", shock, 2, 2}, | |
{"Shock 3", shock, 3, 2}, | |
{"Shock 4", shock, 4, 2}, | |
{"Shock 5", shock, 5, 2}, | |
{"Shock 6", shock, 6, 2}, | |
{"Vibrate short low", vibrate, 0x11, 4}, {"Vibrate med low", vibrate, 0x31, 4}, {"Vibrate long low ", vibrate, 0x51, 4}, | |
{"Vibrate short high", vibrate, 0x19, 4}, {"Vibrate med high", vibrate, 0x39, 4}, {"Vibrate long high", vibrate, 0x59, 4}, | |
}; | |
// | |
/* Web server stuff */ | |
EthernetServer server(80); | |
EthernetClient client; | |
String readRequestLine(){ | |
String line= client.readStringUntil('\n'); | |
//Serial.println(String(line.length()) + ":"+line); | |
if(line.length() < 1) | |
return ""; | |
return line.substring(0, line.length()-1); | |
} | |
String expected_auth = ""; | |
void handleRequest(){ | |
String request = readRequestLine(); | |
String line = ""; | |
String auth =""; | |
// delay(100); | |
do { | |
line = readRequestLine(); | |
// Serial.println(line); | |
if(line.startsWith("Authorization:")) { | |
auth = line; | |
} | |
} while(client.available()); | |
// if(expected_auth.length() == 0){ | |
// Serial.println("Set auth to " + auth.substring(21)); | |
// expected_auth = auth.substring(21); | |
// } | |
// Serial.println("Auth: " + auth); | |
// TODO Figure out why adding this breaks all http stuff. I think it's low memory? | |
// if(!auth.equals("Authorization: Basic " + expected_auth)) { // Snail:password | |
// client.println("HTTP/1.1 401 Unauthorized"); | |
// client.println("WWW-Authenticate: Basic realm=\"\""); | |
// client.println(); | |
// return; | |
// } | |
int left = request.indexOf(' ')+1; | |
int right = request.indexOf(' ', left); | |
if(!left || (right <= left)) return; | |
String path = request.substring(left, right); | |
// /1xy | |
if(path.length() == 4) { | |
int cmd = (path[2]-'0')*10 + path[3]-'0'; | |
Serial.println("Command"); | |
Serial.println(cmd); | |
if(0 <= cmd && cmd < sizeof(COMMANDS)/sizeof(Command)) | |
return respond_bool(COMMANDS[cmd].func(COMMANDS[cmd].arg)); | |
} | |
// /01234567 | |
// if(path.length() == 9) { | |
// byte b1 = hexdecode(path[1])<<4|hexdecode(path[2]); | |
// byte b2 = hexdecode(path[3])<<4|hexdecode(path[4]); | |
// byte b3 = hexdecode(path[5])<<4|hexdecode(path[6]); | |
// byte b4 = hexdecode(path[7])<<4|hexdecode(path[8]); | |
// for(int i = 0 ; i < 6 ; i++) { | |
// digitalWrite(rfTransmitPin, HIGH); | |
// delayMicroseconds(8715); | |
// digitalWrite(rfTransmitPin,LOW); | |
// delayMicroseconds(4305); | |
// sendByte(b1);sendByte(b2);sendByte(b3);sendByte(b4); | |
// delay(2); | |
// } | |
// } | |
return response_page(); | |
} | |
byte hexdecode(char c) { | |
c -= '0'; | |
if(c > 9) return c-7; | |
return c; | |
} | |
void respond_bool(bool b) { | |
client.println("HTTP/1.1 200 OK\nContent-Type: application/json\nConnection: close\n"); | |
client.println(b); | |
} | |
// 1 2 3 4 5 6 7 8 9 0 1 | |
// 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 | |
const char str00[] PROGMEM = "HTTP/1.1 200 OK"; | |
const char str01[] PROGMEM = "Content-Type: text/html"; | |
const char str02[] PROGMEM = "Connection: close"; | |
//const char str03[] PROGMEM = "WWW-Authenticate: Basic realm=\"\""; | |
const char str04[] PROGMEM = ""; | |
const char str05[] PROGMEM = "<html><head><link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'>"; | |
const char str06[] PROGMEM = "<title>Snail's Collar</title><script>"; | |
const char str07[] PROGMEM = "function send(a){o=document.getElementById('o');o.textContent='...';"; | |
const char str08[] PROGMEM = "x=new XMLHttpRequest();x.open('GET','/'+a,0);x.send(null);o.textContent=x.response}"; | |
const char str09[] PROGMEM = "</script></head><body><div class='col-xs-12'>"; | |
const char *const response_lines[] PROGMEM = { | |
str00, str01, str02,/* str03,*/ str04, str05, str06, str07, str08, str09}; | |
char buff[128]; | |
void response_page() { | |
Serial.println("Response"); | |
for(byte i = 0 ; i < 9; i ++) { | |
strcpy_P(buff, (char *)pgm_read_word(&(response_lines[i]))); | |
client.println(buff); | |
Serial.println(buff); | |
} | |
int w = 0; | |
for(int i = 0 ; i < sizeof(COMMANDS)/sizeof(Command) ; i++) { | |
client.print("<button onclick='send("); | |
client.print(100+i); | |
client.print(")' class='btn btn-success col-xs-"); | |
client.print(COMMANDS[i].width); | |
client.print("'>"); | |
client.print(COMMANDS[i].name); | |
client.println("</button>"); | |
w += COMMANDS[i].width; | |
if(w == 12) { | |
w = 0; | |
client.println("</div><div class='col-xs-12'>"); | |
} | |
} | |
client.println("<span id='o'></span></div></body></html>"); | |
} | |
/** Arduino stuff */ | |
void setup() { | |
pinMode(rfTransmitPin, OUTPUT); | |
Serial.begin(115200); | |
byte mac[] ={0x90, 0xA2, 0xDA, 0x0D, 0x02, 0xD9}; | |
Ethernet.begin(mac); | |
server.begin(); | |
} | |
bool b; | |
void loop() { | |
b = false; | |
while(Serial.available()) { | |
b = true; | |
String cmd = Serial.readStringUntil(';'); | |
unsigned int arg = 0; | |
for(int i = 1 ; i < cmd.length() ; i++){ | |
arg = arg*10+cmd[i]-'0'; | |
} | |
Serial.println(String(cmd[0])+" " + arg); | |
switch(cmd[0]) { | |
case 'b': sendCommand(BEEP, arg); break; | |
case 's': sendCommand(SHOCK, arg); break; | |
case 'v': sendCommand(VIBRATE, arg); break; | |
case '_': delayMicroseconds(arg); break; | |
case 'm': delay(arg); break; | |
} | |
} | |
if(b) | |
Serial.println("XXX"); | |
client = server.available(); | |
if (!client) return; | |
handleRequest(); | |
client.stop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment