Created
July 11, 2014 17:15
-
-
Save spirilis/022608d3bb9e8b7ed7a1 to your computer and use it in GitHub Desktop.
IRC of Things bot - IrcBot, Enrf24, Pkt on Tiva-C Connected LaunchPad
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
/* RadioBot - IRC bot with Radio support */ | |
#include <IrcBot.h> | |
#include <Ethernet.h> | |
#include <EthernetClient.h> | |
#include <Enrf24.h> | |
#include <SPI.h> | |
#include <Pkt.h> | |
byte ourMac[] = { 0x52, 0x54, 0xFF, 0xFF, 0xFF, 0x01 }; | |
IrcBot irc; | |
Enrf24 radio(48, 49, 50); | |
Pkt rfif(&radio); // Packet processing engine on top of Enrf24 radio | |
// Add nicknames to this list in order to authorize them to run the "nick" and "io" commands. | |
const char *authnicks[] = { | |
"Spirilis", | |
NULL | |
}; | |
volatile char playground[4096]; // Arbitrary playground for IRC io rd/wr use. | |
const uint8_t basestation_rxaddr[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01}; | |
boolean do_firehose = false; | |
void setup() { | |
int i; | |
IPAddress ip; | |
Serial.setBufferSize(2048, 64); | |
Serial.begin(115200); | |
for (i=0; i < 3; i++) { | |
delay(1000); | |
Serial.print("."); | |
} | |
Serial.println(" BEGIN!"); | |
Serial.println("Performing DHCP:"); | |
if (!Ethernet.begin(ourMac)) { | |
Serial.println("Failed to configure Ethernet using DHCP. Halting."); | |
while(1) delay(1000); | |
} | |
Serial.print("Done; IP = "); | |
ip = Ethernet.localIP(); | |
ip.printTo(Serial); | |
Serial.println(); | |
Serial.println("Initializing IRC bot:"); | |
Serial.print("adding channel #energia as idx = "); Serial.println(irc.addChannel("#energia")); | |
//irc.addChannel("#43oh"); | |
irc.attachOnCommand("hi", HandleHi, NULL); | |
irc.attachOnCommand("die", authnicks, KillBot, NULL); | |
irc.attachOnCommand("roll", RollOver, NULL); | |
irc.attachOnCommand("remember", RememberMe, NULL); | |
irc.attachOnCommand("forget", ForgetMe, NULL); | |
irc.attachOnCommand("nick", authnicks, ChangeNick, NULL); | |
irc.attachOnCommand("io", authnicks, HandleMemoryIO, NULL); | |
irc.attachOnCommand("firehose", authnicks, HandleSwitchOnOff, (void *)&do_firehose); | |
irc.attachOnCommand("radio", authnicks, HandleRadioCmd, NULL); | |
irc.attachOnUserJoin("#energia", "Spirilis", MeetAndGreet, "My Master"); | |
//irc.attachOnUserJoin("#43oh", "Spirilis", MeetAndGreet, "My Master"); | |
Serial.println("Issuing irc.begin():"); | |
irc.begin(); | |
SPI.setModule(4); | |
SPI.setDataMode(SPI_MODE0); | |
SPI.setBitOrder(MSBFIRST); | |
radio.begin(250000, 10); | |
radio.setRXaddress((void *)basestation_rxaddr); | |
radio.setCRC(true, true); | |
rfif.begin(); | |
rfif.attachProgram(0x10, HandleThermocoupleData); // TC data will be printed over IRC | |
} | |
void loop() { | |
static uint32_t lastmillis; | |
uint8_t rfbuf[33]; | |
int len; | |
if (millis()-lastmillis > 10000) { | |
Serial.print("botState = "); Serial.println(irc.getStateStrerror()); | |
lastmillis = millis(); | |
} | |
irc.loop(); // Main worker for IRC bot; runs the whole state machine | |
// Handle incoming RF data | |
if (radio.available(true)) { | |
rfif.loop(); // Run RF Packet RX processor | |
} | |
} | |
void HandleHi(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
char tmpbuf[IRC_NICKUSER_MAXLEN + 32]; | |
Serial.println(">> Executing HandleHi callback handler function"); | |
strcpy(tmpbuf, "Hi there, "); | |
strcat(tmpbuf, nick); | |
strcat(tmpbuf, "!"); | |
irc.sendPrivmsg(chan, nick, tmpbuf); | |
} | |
void KillBot(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
Serial.println(">> Executing KillBot callback handler function"); | |
irc.sendPrivmsg(chan, NULL, "Bye for now!"); | |
irc.end(); | |
} | |
void MeetAndGreet(void *userobj, const char *chan, const char *nick) // OnUserJoin doesn't take a message | |
{ | |
char *override = (char *)userobj, tmpbuf[IRC_NICKUSER_MAXLEN+32]; | |
Serial.print(">> Greeting "); Serial.print(nick); Serial.print(" to "); Serial.println(chan); | |
strcpy(tmpbuf, "Nice to see you again,"); | |
if (override != NULL) { | |
if (*override != ',' && *override != ':' && *override != ';') | |
strcat(tmpbuf, " "); | |
strcat(tmpbuf, override); | |
} else { | |
strcat(tmpbuf, " "); | |
strcat(tmpbuf, nick); | |
} | |
strcat(tmpbuf, "!"); | |
irc.sendPrivmsg(chan, nick, tmpbuf); | |
} | |
void RollOver(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
Serial.println(">> Issuing CTCP ACTION rolls over on his belly."); | |
irc.sendPrivmsgCtcp(chan, "ACTION", "rolls over on his belly."); | |
} | |
void RememberMe(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
if (strstr(message, "me") == NULL) | |
return; | |
if (!irc.attachOnUserJoin(chan, nick, MeetAndGreet, NULL)) { | |
irc.sendPrivmsg(chan, nick, "Sorry, my loyalties have been spread thin, and I simply won't ever remember your name!"); | |
} | |
} | |
void ForgetMe(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
if (strstr(message, "me") == NULL) | |
return; | |
irc.detachOnUserJoin(chan, nick); | |
} | |
void ChangeNick(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
char *tmp1; | |
tmp1 = strstr(message, " "); | |
if (tmp1 != NULL) | |
*tmp1 = '\0'; | |
Serial.print(">> Changing NICK to "); Serial.println(message); | |
irc.setNick(message); | |
} | |
/* I/O Memory command subsystem */ | |
const char hexdigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; | |
void *parseTextToPtr(const char *txt) | |
{ | |
int len = strlen(txt), i = len - 1; | |
long j; | |
char c; | |
uint32_t ptr = 0; | |
if (strncmp(txt, "0x", 2) != 0) { | |
// Probably a decimal address value | |
ptr = atol(txt); | |
} else { | |
if (len > 10) | |
return NULL; | |
while (i > 1) { | |
c = txt[i]; | |
if (c >= '0' && c <= '9') | |
j = c-'0'; | |
else if (c >= 'A' && c <= 'F') | |
j = c-'A'+10; | |
else if (c >= 'a' && c <= 'f') | |
j = c-'a'+10; | |
else | |
return NULL; | |
ptr |= j << ((len-i-1)*4); | |
i--; | |
} | |
} | |
return (void *)ptr; | |
} | |
void convertPtrToText(void *ptr, char *buf) | |
{ | |
uint32_t lval = (uint32_t)ptr; | |
int i; | |
char c[2]; | |
c[1] = '\0'; | |
for (i=0; i<8; i++) { | |
c[0] = hexdigits[ (lval >> ((7-i)*4)) & 0x0F ]; | |
strcat(buf, c); | |
} | |
} | |
void writePtrResult(const char *chan, const char *nick, const char *varname, void *ptr) | |
{ | |
char outbuf[128]; | |
Serial.print(">> writing ptr response for var "); Serial.print(varname); | |
Serial.print(" which is HEX "); Serial.println((uint32_t)ptr, HEX); | |
strcpy(outbuf, varname); | |
strcat(outbuf, " = 0x"); | |
convertPtrToText(ptr, outbuf+strlen(outbuf)); | |
irc.sendPrivmsg(chan, nick, outbuf); | |
} | |
extern uint32_t _text, _etext, _data, _edata, _bss, _ebss, _end; | |
void HandleMemoryIO(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
CmdTok args; | |
void *ptr; | |
int i, j; | |
char outbuf[448], *cur = &outbuf[0]; | |
uint8_t a, b; | |
static boolean write_lock = false; | |
irc.argToken((char *)message, &args); | |
if (args.argc < 3) { | |
// Invalid command | |
irc.sendPrivmsg(chan, nick, "Invalid syntax; insufficient arguments"); | |
return; | |
} | |
if (!strcmp(args.argv[0], "rd")) { | |
ptr = parseTextToPtr(args.argv[1]); | |
j = atoi(args.argv[2]); | |
if (j) { | |
for (i=0; i < j && i < 148; i++) { | |
*cur++ = hexdigits[ *((uint8_t *)ptr+i) >> 4 ]; | |
*cur++ = hexdigits[ *((uint8_t *)ptr+i) & 0x0F ]; | |
*cur++ = ' '; | |
} | |
*cur = '\0'; | |
irc.sendPrivmsg(chan, nick, outbuf); | |
} | |
} else if (!strcmp(args.argv[0], "rda")) { | |
ptr = parseTextToPtr(args.argv[1]); | |
j = atoi(args.argv[2]); | |
if (j) { | |
for (i=0; i < j && i < 148; i++) { | |
*cur++ = *((char *)ptr+i); | |
*cur++ = ' '; | |
} | |
*cur = '\0'; | |
irc.sendPrivmsg(chan, nick, outbuf); | |
} | |
} else if (!strcmp(args.argv[0], "wr")) { | |
ptr = parseTextToPtr(args.argv[1]); | |
// Validate input | |
for (i = 0; i < args.argc-2; i++) { | |
Serial.print(">> Validating input byte specifier: "); Serial.println(args.argv[i+2]); | |
if (strlen(args.argv[i+2]) > 2) { | |
irc.sendPrivmsg(chan, nick, "Invalid byte found in write stream; aborting"); | |
return; | |
} | |
cur = args.argv[i+2]; | |
while (*cur != '\0') { | |
if (*cur < '0' || (*cur > '9' && *cur < 'A') || (*cur > 'F' && *cur < 'a') || (*cur > 'f')) { | |
irc.sendPrivmsg(chan, nick, "Invalid byte found in write stream; aborting"); | |
return; | |
} | |
cur++; | |
} | |
} | |
// Input validated; commit writes | |
for (i = 0; i < args.argc-2; i++) { | |
cur = args.argv[i+2]; | |
b = 0; | |
for (j=strlen(cur)-1; j >= 0; j--) { | |
if (cur[j] >= '0' && cur[j] <= '9') | |
a = cur[j] - '0'; | |
else if (cur[j] >= 'A' && cur[j] <= 'F') | |
a = cur[j] - 'A' + 10; | |
else if (cur[j] >= 'a' && cur[j] <= 'f') | |
a = cur[j] - 'a' + 10; | |
b |= a << ((1-j)*4); | |
} | |
Serial.print(">> Writing hex value "); Serial.print(b, HEX); Serial.print(" to address "); Serial.println((uint32_t)ptr, HEX); | |
if (!write_lock) | |
*(((uint8_t *)ptr)+i) = b; | |
} | |
if (write_lock) | |
irc.sendPrivmsg(chan, nick, "Write ignored due to lock."); | |
else | |
irc.sendPrivmsg(chan, nick, "Write committed."); | |
} else if (!strcmp(args.argv[0], "wrlock")) { | |
if (!strcmp(args.argv[1], "on")) { | |
write_lock = true; | |
} else if (!strcmp(args.argv[1], "off")) { | |
write_lock = false; | |
} | |
} else if (!strcmp(args.argv[0], "var")) { | |
if (!strcmp(args.argv[1], "ptr")) { | |
if (!strcmp(args.argv[2], "playground")) { | |
writePtrResult(chan, nick, "playground", (void *)playground); | |
return; | |
} | |
if (!strcmp(args.argv[2], "authnicks")) { | |
writePtrResult(chan, nick, "authnicks", (void *)authnicks); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_end")) { | |
writePtrResult(chan, nick, "_end", (void *)&_end); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_bss")) { | |
writePtrResult(chan, nick, "_bss", (void *)&_bss); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_ebss")) { | |
writePtrResult(chan, nick, "_ebss", (void *)&_ebss); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_data")) { | |
writePtrResult(chan, nick, "_data", (void *)&_data); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_edata")) { | |
writePtrResult(chan, nick, "_edata", (void *)&_edata); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_text")) { | |
writePtrResult(chan, nick, "_text", (void *)&_text); | |
return; | |
} | |
if (!strcmp(args.argv[2], "_etext")) { | |
writePtrResult(chan, nick, "_etext", (void *)&_etext); | |
return; | |
} | |
} | |
} | |
} | |
void HandleSwitchOnOff(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
boolean *sw = (boolean *)userobj; | |
if (!strcmp(message, "on")) { | |
*sw = true; | |
} | |
if (!strcmp(message, "off")) { | |
*sw = false; | |
} | |
} | |
void HandleRadioCmd(void *userobj, const char *chan, const char *nick, const char *message) | |
{ | |
char outbuf[256]; | |
CmdTok args; | |
irc.argToken((char *)message, &args); | |
if (!strcmp(args.argv[0], "state")) { | |
strcpy(outbuf, "nRF24L01+ State: "); | |
strcat(outbuf, radio_state_to_string(radio.radioState())); | |
irc.sendPrivmsg(chan, nick, outbuf); | |
} | |
} | |
const char * radio_state_to_string(uint8_t status) | |
{ | |
switch (status) { | |
case ENRF24_STATE_NOTPRESENT: | |
return "NO TRANSCEIVER PRESENT"; | |
break; | |
case ENRF24_STATE_DEEPSLEEP: | |
return "DEEP SLEEP <1uA power consumption"; | |
break; | |
case ENRF24_STATE_IDLE: | |
return "IDLE module powered up w/ oscillators running"; | |
break; | |
case ENRF24_STATE_PTX: | |
return "Actively Transmitting"; | |
break; | |
case ENRF24_STATE_PRX: | |
return "Receive Mode"; | |
break; | |
default: | |
return "UNKNOWN STATUS CODE"; | |
} | |
} | |
void HandleThermocoupleData(const uint8_t progID, const int len, const void *buffer) | |
{ | |
int16_t tcC, cjC, tcF, cjF; | |
char outbuf[128], tmp[10]; | |
const uint8_t *cbuf = (const uint8_t *)buffer; | |
if (len != 6) | |
return; // invalid length | |
/* Data format: | |
* | |
* 1 2 2 1 | |
* +----+--------------+-----------------+-------+ | |
* | ID | TCtemp(C) | ColdJuncTemp(C) | Flags | | |
* +----+--------------+-----------------+-------+ | |
*/ | |
if (do_firehose) { | |
strcpy(outbuf, "TC"); | |
itoa(cbuf[0], outbuf+2, 10); | |
strcat(outbuf, " - "); | |
memcpy(&tcC, cbuf+1, 2); | |
memcpy(&cjC, cbuf+3, 2); | |
tcF = tcC * 9 / 5 + 32; | |
cjF = cjC * 9 / 5 + 32; | |
itoa(tcC, tmp, 10); | |
strcat(outbuf, tmp); strcat(outbuf, "C ("); | |
itoa(tcF, tmp, 10); | |
strcat(outbuf, tmp); strcat(outbuf, "F) - Cold Junction "); | |
itoa(cjC, tmp, 10); | |
strcat(outbuf, tmp); strcat(outbuf, "C ("); | |
itoa(cjF, tmp, 10); | |
strcat(outbuf, tmp); strcat(outbuf, "F)"); | |
irc.sendPrivmsg("#energia", "Spirilis", outbuf); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment