Skip to content

Instantly share code, notes, and snippets.

@theepicsnail
Last active August 1, 2020 03:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theepicsnail/48e76f9d293c70f48d2633a383c20ba5 to your computer and use it in GitHub Desktop.
Save theepicsnail/48e76f9d293c70f48d2633a383c20ba5 to your computer and use it in GitHub Desktop.
#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