adamkdean

software engineering

WASD Control for R/C Car using Arduino

By Adam K Dean on

I will comment this more when I get time, just posting the code for a forum right now!

/* Author: Adam K Dean (imdsm.blogspot.org) */
using System;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace SerialRC_Car
{
    public enum ThrottleState { Forward, Reverse, None };
    public enum TurnState { Left, Right, None };

    public partial class frmMain : Form
    {
        private Thread readThread;
        private SerialPort _serialPort;
        private bool _continue;

        private ThrottleState throttleState = ThrottleState.None;
        private TurnState turnState = TurnState.None;


        public frmMain()
        {
            InitializeComponent();
            Toggle(false);
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            comboPort.Items.AddRange(SerialPort.GetPortNames());
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            try
            {
                _serialPort = new SerialPort();

                // Allow the user to set the appropriate properties.
                _serialPort.PortName = comboPort.SelectedItem.ToString();
                _serialPort.BaudRate = 9600;
                _serialPort.Parity = Parity.None;
                _serialPort.Handshake = Handshake.None;

                // Set the read/write timeouts
                _serialPort.ReadTimeout = 500;
                _serialPort.WriteTimeout = 500;

                readThread = new Thread(Read);
                _serialPort.Open();
                _continue = true;
                readThread.Start();

                Toggle(true);
            }
            catch (Exception ex)
            {
                WriteLogText("Exception: {0}", ex.Message);
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            Toggle(false);

            _continue = false;
            readThread.Join();
            _serialPort.Close();
        }

        private void frmMain_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.W) btnW_MouseDown(this, null);
            else if (e.KeyCode == Keys.S) btnS_MouseDown(this, null);
            else if (e.KeyCode == Keys.A) btnA_MouseDown(this, null);
            else if (e.KeyCode == Keys.D) btnD_MouseDown(this, null);
        }

        private void frmMain_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.W) btnW_MouseUp(this, null);
            else if (e.KeyCode == Keys.S) btnS_MouseUp(this, null);
            else if (e.KeyCode == Keys.A) btnA_MouseUp(this, null);
            else if (e.KeyCode == Keys.D) btnD_MouseUp(this, null);
        }

        #region Throttle Control
            #region Forward - W
            private void btnW_MouseDown(object sender, MouseEventArgs e)
            {
                if (throttleState != ThrottleState.Forward)
                {
                    if (throttleState == ThrottleState.Reverse) btnS_MouseUp(this, null);
                    throttleState = ThrottleState.Forward;
                    _serialPort.Write("blink;w1;");
                }
            }

            private void btnW_MouseUp(object sender, MouseEventArgs e)
            {
                throttleState = ThrottleState.None;
                _serialPort.Write("blink;w0;");
            }
            #endregion

            #region Reverse - S
            private void btnS_MouseDown(object sender, MouseEventArgs e)
            {
                if (throttleState != ThrottleState.Reverse)
                {
                    if (throttleState == ThrottleState.Forward) btnW_MouseUp(this, null);
                    throttleState = ThrottleState.Reverse;
                    _serialPort.Write("blink;s1;");
                }
            }

            private void btnS_MouseUp(object sender, MouseEventArgs e)
            {
                throttleState = ThrottleState.None;
                _serialPort.Write("blink;s0;");
            }
            #endregion
        #endregion

        #region Steering Control
            #region Left - A
            private void btnA_MouseDown(object sender, MouseEventArgs e)
            {
                if (turnState != TurnState.Left)
                {
                    if (turnState == TurnState.Right) btnD_MouseUp(this, null);
                    turnState = TurnState.Left;
                    _serialPort.Write("blink;a1;");
                }
            }

            private void btnA_MouseUp(object sender, MouseEventArgs e)
            {
                turnState = TurnState.None;
                _serialPort.Write("blink;a0;");
            }
            #endregion

            #region Right - D
            private void btnD_MouseDown(object sender, MouseEventArgs e)
            {
                if (turnState != TurnState.Right)
                {
                    if (turnState == TurnState.Left) btnA_MouseUp(this, null);
                    turnState = TurnState.Right;
                    _serialPort.Write("blink;d1;");
                }
            }

            private void btnD_MouseUp(object sender, MouseEventArgs e)
            {
                turnState = TurnState.None;
                _serialPort.Write("blink;d0;");
            }
            #endregion
        #endregion

        private void WriteLogText(string format, params object[] args)
        {
            WriteLogText(string.Format(format, args));
        }
        private void WriteLogText(string text)
        {
            if (InvokeRequired)
            {
                Action<string> del = new Action<string>(WriteLogText);
                Invoke(del, text);
                return;
            }

            if (txtLog.Text.Length == 0) txtLog.Text = text;
            else txtLog.Text += "\r\n" + text;
            if (txtLog.Text.Length > 5000)
                txtLog.Text = txtLog.Text.Substring(txtLog.Text.Length - 5000);
            txtLog.SelectionStart = txtLog.Text.Length;
            txtLog.ScrollToCaret();
        }

        public void Read()
        {
            while (_continue)
            {
                try
                {
                    string message = _serialPort.ReadLine();
                    WriteLogText("RECV> {0}", message);
                }
                catch (TimeoutException) { }
            }
        }

        private void Toggle(bool enabled)
        {
            comboPort.Enabled = !enabled;
            btnLoad.Enabled = !enabled;
            btnStop.Enabled = enabled;
            btnW.Enabled = enabled;
            btnA.Enabled = enabled;
            btnS.Enabled = enabled;
            btnD.Enabled = enabled;
        }
    }
}

Arduino code:

/* Author: Adam K Dean (imdsm.blogspot.org) */

/* Control for RC Car
 * Adam K Dean 2011
 * adamkdean.co.uk
 */

int led = 13;

int fwd = 11;
int rev = 10;
int left = 9;
int right = 8;

void setup()
{
  pinMode(led, OUTPUT);

  pinMode(fwd, OUTPUT);
  pinMode(rev, OUTPUT);
  pinMode(left, OUTPUT);
  pinMode(right, OUTPUT);

  Serial.begin(9600);
  Serial.println("Started..");
}

void loop()
{
  String line = readLineIfAvailable();

  if (line.length() > 0 ) Serial.println(line);

  if (line == "blink")
  {
    digitalWrite(led, HIGH);
    delay(10);
    digitalWrite(led, LOW);
  }

  // forward - w
  if (line == "w1")
  {
    digitalWrite(fwd, HIGH);
  }
  else if (line == "w0")
  {
    digitalWrite(fwd, LOW);
  }

  // reverse - s
  if (line == "s1")
  {
    digitalWrite(rev, HIGH);
  }
  else if (line == "s0")
  {
    digitalWrite(rev, LOW);
  }

  // left - a
  if (line == "a1")
  {
    digitalWrite(left, HIGH);
  }
  else if (line == "a0")
  {
    digitalWrite(left, LOW);
  }

  // right - d
  if (line == "d1")
  {
    digitalWrite(right, HIGH);
  }
  else if (line == "d0")
  {
    digitalWrite(right, LOW);
  }  

  delay(10);
}

String readLineIfAvailable()
{
  String readstr;

  while(Serial.available())
  {
    if (Serial.available() > 0)
    {
      char c = Serial.read();

      if (c == ';') return readstr;
      else readstr += c;
    }
  }
}

No definition found for Table yahoo.finance.quotes

By Adam K Dean on

I am writing a bit of code to grab stock data and Yahoo's CSV data source is absolutely terrible. Commas in the middle of data in a CSV file? madness!

So I turned to YQL the only other source really, without scraping html.. and have strangely found that Yahoo's stock data table has disappeared. "No definition found for Table yahoo.finance.quotes". A few other people have also found this so after less than 30 seconds of digging, I found an answer:

Add &env=http://datatables.org/alltables.env to your query.

For example: http://query.yahooapis.com/v1/public/yql?q=select * from yahoo.finance.quotes where symbol in ("YHOO","AAPL","GOOG","MSFT")&env=http://datatables.org/alltables.env

There you go. Enjoy.

Excel Formula - Nice Currency!

By Adam K Dean on

I hate it in Excel when my currency has to be either spaced out nicely OR red when negative. There is the currency, which has nice colours, for negatives, which is good. But then the accounting format has nice spacing, with the pound sign on the left.

Well, now I can have both!

_-£* #,##0.00;[Red]-£* #,##0.00

Woop!

Example

MySql Version "Correlated Sub-Queries and Group By Unions"

By Adam K Dean on

If you look at this SQL article [old link removed], you may either be impressed or insult me for bad coding practise. FYI, it does run on 1000 rows in < 1 second. When I was using MsSql (before I migrated to CentOS/Mono/MySql) I sometimes had to go to extortionate amounts of effort to get stuff done, but with MySql it all seems, a bit too easy.. So from the giant SQL that you saw in the previous article, I can do all that on the same tables (bar one change) with the following SQL:

SELECT t_Hosts.Id AS Id, t_Records.Id AS RecordId, Hostname, 

Frequency, PingStatus, PingLatency, HttpStatusCode, 

HttpOnlineStatus, HttpLatency, CheckDate

FROM t_Hosts 

LEFT JOIN t_Records ON t_Hosts.Id = t_Records.HostId

WHERE t_Records.CheckDate IS NULL

OR t_Records.CheckDate < DATE_SUB(NOW(), INTERVAL t_Hosts.Frequency MINUTE);

This happens to have caused more trouble than it's worth, so it's back to a datetime now.

Also, make sure that you add an alias for the second Id or it may overwrite the first tables Id!

Edit: It would seem this doesn't work, and is actually flawed. My old logic (@mcfly version) is actually the way to go, not streakys stupid left join crap!

Below is the MySQL Version.

SELECT t_Hosts.Id AS Id, MAX(Hostname) AS Hostname, MAX(Frequency) AS Frequency
FROM t_Hosts
LEFT JOIN t_Records
ON t_Hosts.Id = t_Records.HostId 
GROUP BY t_Hosts.Id
HAVING MAX(CheckDate) <= DATE_SUB(NOW(), INTERVAL MAX(Frequency) MINUTE)
UNION
SELECT Id, Hostname, Frequency
FROM t_Hosts
WHERE (
   Id NOT IN (
      SELECT HostId
      FROM t_Records
      WHERE (HostId IS NOT NULL)
   )
)

New String.Format for C - strrep2

By Adam K Dean on

Following on from the problems I had with my previous string.format C function, I decided to rewrite it today under heavy valgrind observation.

So I re-wrote it and now get the all clear from valgrind:

==25693== HEAP SUMMARY: ==25693== in use at exit: 0 bytes in 0 blocks ==25693== total heap usage: 43 allocs, 43 frees, 161 bytes allocated ==25693== ==25693== All heap blocks were freed -- no leaks are possible

/*
 * File:   strrep2.c
 * Author: Adam K Dean
 * Description: string replacement (replication of string.replace for C)
 *
 * Original created on 11 January 2011, 10:01
 * This version (2) created on 14 January 2011, 9:36
 */

// usage
int main()
{
    char str[] = "ab{0}def{1}hi{2}";
    char *rep = strrep2(str, 3, "c", "g", "j");

    printf("%s\n", rep);

    free(rep);

    return (EXIT_SUCCESS);
}

char *strrep2(char *format, int argc, char *argv, ...)
{
    char *args[argc];
    char *tmp_left, *tmp_right, *arg, *fstr;
    char *start, *end, *param;
    int l_len, r_len, f_sz, argn;

    va_list marker;
    va_start(marker, *argv);
    for(int i = 0; i < argc; i++)
    {
        args[i] = (char *)calloc(strlen(argv) + 1, sizeof(char));
        strcpy(args[i], argv);
        argv = va_arg(marker, char *);
    }
    va_end(marker);

    fstr = (char *)calloc(strlen(format) + 1, sizeof(char));
    strcpy(fstr, format);

    for(int c = 1; c < strlen(fstr) - 1; c++)
    {
        start = (char *)calloc(2, sizeof(char));
        param = (char *)calloc(2, sizeof(char));
        end = (char *)calloc(2, sizeof(char));
        strncpy(start, &fstr[c-1], 1);
        strncpy(param, &fstr[c], 1);
        strncpy(end, &fstr[c+1], 1);

        if (start[0] == '{' && end[0] == '}' && isdigit(param[0]))
        {
            argn = atoi(&param[0]);
            if (argn < 0 || argn >= argc) continue;

            arg = args[argn];

            l_len = c - 1;
            r_len = (strlen(fstr) - 3) - l_len;

            tmp_left = (char *)calloc(l_len + 1, sizeof(char));
            tmp_right = (char *)calloc(r_len + 1, sizeof(char));

            strncpy(tmp_left, fstr, l_len);
            strncpy(tmp_right, &fstr[c + 2], r_len);

            f_sz = l_len + r_len + strlen(arg);

            fstr = (char *)realloc(fstr, (f_sz + 1) * sizeof(char));

            strcpy(fstr, tmp_left);
            strcpy(&fstr[l_len], arg);
            strcpy(&fstr[l_len + strlen(arg)], tmp_right);

            free(tmp_left);
            free(tmp_right);
        }

        free(start);
        free(param);
        free(end);
    }

    // free args
    for(int i = 0; i < argc; i++) free(args[i]);

    return fstr;
}