Jump to content
43oh

Recommended Posts

Ok this is just silly, but I had to do it.

 

Simple IRC client that pops into #energia, says hi and runs.

/* IRC Example
 * Simple IRC client which connects gracefully, joins #energia and says a simple Hello before quitting and indefinitely sleeping.
 */
#include <Ethernet.h>
#include <IPAddress.h>
#include <string.h>

byte ourMac[] = { 0x52, 0x54, 0xFF, 0xFF, 0xFF, 0x01 };
EthernetClient client;

const char *ircserver_name = "chat.freenode.net";

//#define IRC_USE_IP_NOT_DNS
//IPAddress ircserver(62,231,75,133);
// ^ Required for connecting through an SSH tunnel for getting around a firewall
// Comment out #define IRC_USE_IP_NOT_DNS to use the ircserver_name DNS to connect.

#define IRCSERVER_PORT 6667

const char *irc_nick = "SpirTivaLP";
const char *irc_user = "tm4c129";
const char *irc_user_description = "Energia Warrior";
const char *irc_channel = "#energia";

void dispatch_composite_message(uint8_t, int);
void process_inbound_message(uint8_t, int);

void setup()
{
  IPAddress ip;
  
  Serial.setBufferSize(2048, 64);
  Serial.begin(115200);
  
  // Wait for USR_SW1 to be pressed before continuing (10ms resolution polling the button)...
  // This just gives me time to open the Serial Monitor before proceeding.
  pinMode(USR_SW1, INPUT_PULLUP);
  uint32_t lastmillis = millis();
  while (digitalRead(USR_SW1)) {
    if ( (millis() - lastmillis) > 1000 ) {
      Serial.println("Waiting for USR_SW1 press to begin...");
      lastmillis = millis();
    }
    delay(10);
  }
  
  // Initialize network.
  Serial.println("\n\nDHCP IRC Client Example");  
  Serial.print("DHCP init: ");
  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("\n");
}

volatile boolean is_connected = false;
volatile int prot_stage = 0;
volatile boolean prot_stage_latched = false;
/* ^ Determines if the current event stage has been carried out, in which
 * case we're waiting for some reply or some other event to proceed to
 * the next event stage.
 */

// IRC protocol event stages
#define PROT_STAGE_WAITINIT 0
#define PROT_STAGE_NICK 1
#define PROT_STAGE_USER 2
#define PROT_STAGE_JOIN 3
#define PROT_STAGE_SAYHELLO 4
#define PROT_STAGE_QUIT 5
#define PROT_STAGE_DONE 6

void loop()
{
  uint8_t buf[2049];
  int len;
  
  // Main loop
  if (!is_connected || !client.connected()) {
    Serial.print("Connecting to: ");
    Serial.println(ircserver_name);
#ifdef IRC_USE_IP_NOT_DNS
    if (client.connect(ircserver, IRCSERVER_PORT)) {
#else
    if (client.connect(ircserver_name, IRCSERVER_PORT)) {
#endif
      Serial.println("connected\n");
      is_connected = true;
      prot_stage = PROT_STAGE_WAITINIT;
    } else {
      is_connected = false;
      Serial.println("Failed; pausing 5 seconds before retrying");
      delay(5000);
    }
  } else {
    // Process incoming data first
    if (client.available()) {
      if ((len = client.read(buf, 2048)) > 0) {
        buf[len] = '\0';
        Serial.print("Inbound msg: ");
        Serial.write(buf, len);
        Serial.println();
        dispatch_composite_message(buf, len);
        // TODO: Use a ring-buffer with this code as the producer and dispatch_composite_message as the consumer.
      }
    }
    
    // Check protocol event stage and perform outbound messages
    switch (prot_stage) {
      case PROT_STAGE_NICK:
        if (!prot_stage_latched) {
          Serial.println("Registering NICK-\n");
          // Compose NICK command
          buf[0] = 0;
          strcat((char *)buf, "NICK ");
          strcat((char *)buf, (char *)irc_nick);
          strcat((char *)buf, "\r\n");
          client.print((char *)buf);
          prot_stage++;  // No reply needed to advance to next step
        }
        break;
  
      case PROT_STAGE_USER:
        if (!prot_stage_latched) {
          Serial.println("Registering USER-\n");
          client.print("USER ");
          client.print(irc_nick);
          client.print(" ");
          client.print(irc_user);
          client.print(" ");
          client.print(ircserver_name);
          client.print(" :");
          client.print(irc_user_description);
          client.print("\r\n");
          prot_stage_latched = true;
          // Must wait for RPY_ENDOFMOTD before proceeding
        }
        break;
  
      case PROT_STAGE_JOIN:
        if (!prot_stage_latched) {
          Serial.print("Joining "); Serial.print(irc_channel); Serial.println("-");
          client.print("JOIN ");
          client.print(irc_channel);
          client.print("\r\n");
          prot_stage_latched = true;
          // Must wait for RPY_TOPIC before proceeding
        }
        break;
  
      case PROT_STAGE_SAYHELLO:
        if (!prot_stage_latched) {
          Serial.print("Writing to "); Serial.print(irc_channel); Serial.println("-");
          client.print("PRIVMSG ");
          client.print(irc_channel);
          client.print(" :");
          client.print("Hi everyone!  I am a TI TM4C129 LaunchPad connecting to IRC and sending you a brief message.");
          client.print("  Currently using Energia release ");
          client.print(12, DEC);
          client.println(".\r\n");
          prot_stage++;  // No reply needed to proceed with the next step
        }
        break;

      case PROT_STAGE_QUIT:
        if (!prot_stage_latched) {
          Serial.println("Issuing QUIT-\n");
          client.print("QUIT :Bye for now!\r\n");
          prot_stage++;
        }
        break;
  
      case PROT_STAGE_DONE:
        Serial.println("Done.");
        while (1)
          delay(1000);
        break;
    }  /* switch(prot_stage) */
  }  /* if (is_connected) */
} /* loop() */

/* Process a packet into a sequence of \r\n-terminated lines; Note this should be replaced
 * with a ring-buffer design, as data which flows across multiple packets will not be
 * processed correctly; the last request will be truncated and the beginning of the next
 * request will be handled improperly.
 */
void dispatch_composite_message(uint8_t *buf, int len)
{
  uint8_t *bufptr = buf;
  uint8_t *nl = (uint8_t *)strstr((char *)bufptr, "\r\n");
  int sublen = 0;
  
  while (nl != NULL) {
    sublen = (int) (nl - bufptr);
    process_inbound_message(bufptr, sublen);
    bufptr += sublen + 2;
    if ( (bufptr - buf) >= len )
      return;
    
    nl = (uint8_t *)strstr((char *)bufptr, "\r\n");
  }
}

/* Process individual line. */
void process_inbound_message(uint8_t *buf, int sublen)
{
  uint8_t *arg1, *arg2;
  int len;
  
  // Process inbound message
  buf[sublen] = '\0';
  
  arg1 = (uint8_t *)strstr((char *)buf, " ");
  if (arg1 == NULL)
    return;
  *arg1 = '\0';
  arg1++;
  int i=0;
  while (arg1[i] != ' ' && arg1[i] != '\0')
    i++;
  arg1[i] = '\0';
  if ( (arg1 + i - buf) >= sublen )
    arg2 = NULL;
  else
    arg2 = arg1 + i + 1;

  Serial.print("Reply ARG: "); Serial.println((char *)arg1);
  
  // WAITINIT waits for any sort of reply from the IRC server indicating it has connected
  if (prot_stage == PROT_STAGE_WAITINIT) {
    prot_stage++;
    prot_stage_latched = false;
    return;
  }
  
  // Perform PING reply
  if (!strcmp((char *)buf, "PING")) {
    Serial.print("Received PING "); Serial.print((char *)arg1); Serial.println("; sending PONG");
    client.print("PONG ");
    if (arg1[0] == ':')
      client.print((char *)(arg1+1));
    else
      client.print((char *)arg1);    
    client.print("\r\n");
  }
  
  // RPY_ENDOFMOTD to advance past USER
  if (prot_stage == PROT_STAGE_USER && !strcmp((char *)arg1, "376")) {
    prot_stage++;
    prot_stage_latched = false;
  }
  
  // RPY_TOPIC to advance past JOIN
  if (prot_stage == PROT_STAGE_JOIN && !strcmp((char *)arg1, "332")) {
    prot_stage++;
    prot_stage_latched = false;
  }
  
  
}

Probably a lot of things I can improve on, not the least of which is a proper ring-buffer for the inbound data and a better parser.  But it works:

14:17 -!- SpirTivaLP [~SpirTivaL@<hostname_redacted>] has joined #energia
14:17 < SpirTivaLP> Hi everyone!  I am a TI TM4C129 LaunchPad connecting to IRC and sending you a brief message.  Currently using Energia release 12.
14:17 -!- SpirTivaLP [~SpirTivaL@<hostname_redacted>] has quit [Client Quit]


We'll call it the beginnings of a Tiva-C IRC bot...

Link to post
Share on other sites

That opens up a strange environment....

Put this with the ECU thing, over Bluetooth to my phone, and now I can get chat messages about my engine's state on my watch and headset?

 

"Warning. Exceeding posted speed limit."

 

 

Maybe it could be set to do speech to text and I say "Increase fuel duration 1 millisecond" and it reprograms my ECU?

 

And then someone joins the channel and says "Kill the engine" and I'm done for the day. :D

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...