Difference between revisions of "Talk:Cheesoid"

From Nottinghack Wiki
Jump to navigation Jump to search
(talkpage)
 
(32 intermediate revisions by 3 users not shown)
Line 57: Line 57:
 
Good examples of the use of sphinx: -
 
Good examples of the use of sphinx: -
 
* http://perlbox.sourceforge.net/
 
* http://perlbox.sourceforge.net/
 +
 +
If Sphinx 4 works on the eeePC (try the demos) then go ahead and use it from Java.
 +
* http://cmusphinx.sourceforge.net/sphinx4/doc/ProgrammersGuide.html
  
 
=== Conversations and dialogue ===
 
=== Conversations and dialogue ===
 
In order to have a meaningful (preferably amusing and slightly uncanny) interaction with humans there needs to be a dialogue and some involvement of non-verbal communications: perhaps some shared experience, empathy, etc. We can fake a lot of things to push the human participant closer to the goal!
 
In order to have a meaningful (preferably amusing and slightly uncanny) interaction with humans there needs to be a dialogue and some involvement of non-verbal communications: perhaps some shared experience, empathy, etc. We can fake a lot of things to push the human participant closer to the goal!
 +
* New status phrases (with equivalents and random or circular selection) - use emotional terms
 +
** "I don't know where I am"
 +
** "I'm not happy"
 +
** "I'm scared"
 +
** "I'm upset"
 +
** "this is all wrong" - "I don't like this situation"
 +
** "what is this place?"
 +
** "where am I? I can't see and I can't move"
 +
** "I haven't been here before"
 +
** "I need to check the petrol but I can't get there"
 +
** "I need to check the cheese but I can't get there"
 +
** "I can't move. Someone will have to carry me"
 +
** "I'm very lonely. Can somebody talk to me" (on IRC/or actual voice)
 +
** "There's nothing here"
 +
** "This is terrible. Is there no god?"
 +
* Wake up phrases for Map module...
 +
** "Do I know this place?"
 +
** "Is this [location name]?"
 +
** "Can somebody tell me where I am?"
  
 
= Mobility =
 
= Mobility =
Line 220: Line 242:
 
</div>
 
</div>
 
This sketch is a munge of http://letsmakerobots.com/node/2074 and http://itp.nyu.edu/physcomp/Labs/DCMotorControl
 
This sketch is a munge of http://letsmakerobots.com/node/2074 and http://itp.nyu.edu/physcomp/Labs/DCMotorControl
 +
 +
OK, after a couple of nights at Hackspace I now have the motors mounted and coupled to the wheel assemblies but the whole thing is way too weak to drive the wheels when using the big 12v lead-acid batteries I have. It's a little better with the bench power supply at 12v and it's how I'd want it with the bench supply at 14v. I think there's multiple problems here: the wheel assemblies are heavy and the shed-built motor brackets are inaccurate, as are the polymorph couplings (an idea borrowed from here http://letsmakerobots.com/node/15354). I thought that the wheel assembly would be necessary to take some of the load off the motor axles but I see quite a lot of setups with the wheels attached directly to the motors as in this example that also uses 38mm motors: http://www.pololu.com/catalog/product/730
 +
 +
I'm tempted to redesign around some direct wheel couplings and purchased wheels even though I love the CD wheel idea.
 +
* http://www.active-robots.com/2-x-universal-hub-6mm-shaft.html
 +
* http://www.active-robots.com/motors-wheels/motor-hardware/dc-motor-hardware/2-x-pololu-universal-hub-6mm-shaft.html
 +
* http://www.skpang.co.uk/catalog/pololu-universal-aluminum-mounting-hub-for-6mm-shaft-pair-p-800.html
 +
 +
I've joined the letsmakerobots.com forum and I'm going to ask some advice there.
 +
 +
--[[User:Msemtd|Michael Erskine]] 09:18, 8 October 2011 (EST)
 +
 +
== MCU1 Motor Control ==
 +
 +
MCU1 will drive each motor via the SN754410 with three signals: Motor_Logic_1, Motor_Logic_2, and Motor_Enable (corresponding to the SN754410 pins). The Motor_Enable signals will be PWM thus using in total 2 PWM outputs plus 4 digital outputs. This allows a simple interface to the SN754410.
 +
 +
Control is via serial from the eeePC giving parameters
 +
of direction and speed for both motors at once. The motors can be put in hold
 +
with a stop command. The messages need to be quick for MCU1 to read
 +
and interpret but also easily human-readable.
 +
 +
  D[L-dir][L-speed][R-dir][R-speed]
 +
 
 +
  direction = 1 char, 'F' = forwards, 'B' = backwards, 'X' = hold
 +
  speed = 3 ASCII decimal digits in range 000 to 255 left zero padded
 +
 +
Examples: -
 +
* DF255F255 = full speed ahead
 +
* DF127F127 = half speed ahead
 +
* DF000F000 = freewheel?
 +
* DX000X000 = hold stop
 +
* DB255F255 = fast rotate left
 +
* DF255B255 = fast rotate right
 +
* DX000F255 = fast pivot left
 +
 +
The PWM will continue unless stopped so we should have a timeout on MCU1
  
 
= Bodywork =
 
= Bodywork =
Line 236: Line 294:
 
* cylinder case mods
 
* cylinder case mods
 
** speakers in mouth plate/grill
 
** speakers in mouth plate/grill
 +
*** possible replacement speaker: USB rechargeable single speaker $6.38
 +
*** http://lightake.com/detail.do/sku.Audio_Artist_Hamburger_Mini_Speaker___Black-41485
 
** side mount for MCU1 and support boards (temporary?)
 
** side mount for MCU1 and support boards (temporary?)
 
** top for beacon - keep on side for now
 
** top for beacon - keep on side for now
** mounting of cylinder on chassis  
+
** mounting of cylinder on chassis -- 2x panel circular base (inner diameter of shell) cut with jigsaw
 +
* pizza box - vertical holder
  
 
* motor mountings
 
* motor mountings
Line 261: Line 322:
 
MCU1 PIN usage summary table [https://docs.google.com/spreadsheet/ccc?key=0AsK9pfCIv4uddDlkSkxOMjNaTnlBOWpvUERUYWZGbUE&hl=en_GB here on Google Docs]
 
MCU1 PIN usage summary table [https://docs.google.com/spreadsheet/ccc?key=0AsK9pfCIv4uddDlkSkxOMjNaTnlBOWpvUERUYWZGbUE&hl=en_GB here on Google Docs]
  
MCU1 software
+
=== MCU1 software ===
* TODO motor drive code and motor drive commands from PC
+
<div style ="height:200px;overflow-x:hidden;overflow-y:auto;border: 4px solid green;">
 +
<syntaxhighlight lang="java" line="GESHI_FANCY_LINE_NUMBERS" enclose="div">
 +
/*******************************************************************/
 +
/*                          CHEESOID MCU1                          */
 +
/*******************************************************************/
 +
 
 +
// I/O Pin definitions...
 +
// The mode switch is on a digital input...
 +
const int PIN_MODESW = 4;
 +
// Two LEDs are used for eyes on two digital pins...
 +
const int PIN_EYE1 = 7;
 +
const int PIN_EYE2 = 8;
 +
// Built-in LED on pin 13 used for watchdog status...
 +
const int PIN_WDLED = 13;
 +
// Motor enable/speed PWM outputs...
 +
const int PIN_M1SPD = 9;
 +
const int PIN_M2SPD = 10;
 +
// Motor control logic digital outputs...
 +
const int PIN_M1C1 = 14;
 +
const int PIN_M1C2 = 15;
 +
const int PIN_M2C1 = 16;
 +
const int PIN_M2C2 = 17;
 +
// Bumper inputs...
 +
const int PIN_BMP1 = 18;
 +
const int PIN_BMP2 = 19;
 +
// Soft serial to MCU2...
 +
const int PIN_MCU2TX = 11;
 +
 
 +
 
 +
// Last watchdog time tracking...
 +
unsigned long wdoglast = 0;
 +
// main loop has 10ms sleeps...
 +
const int naptime = 10;
 +
// the eyes flash every 60 loops = 600ms...
 +
int eyefreq = 60;
 +
// info is sent out on the serial port every 90 loops = 900ms...
 +
int reportfreq = 90;
 +
// a simple loop counter is used to decide what to do...
 +
unsigned int loopcounter = 0;
 +
// mode switch state...
 +
int mode = 0;
 +
 
 +
// System counters...
 +
// Max number of bytes read in one go...
 +
#define CTR_MAXREAD 0
 +
// number of buffer overflows avoided...
 +
#define CTR_OVERFLOW 1
 +
// Number of bytes received...
 +
#define CTR_BYTES 2
 +
// Number of unknown message commands...
 +
#define CTR_UNKMSG 3
 +
// Number of unknown message commands...
 +
#define CTR_WDOGTO 4
 +
static int counters[4];
 +
// watchdog timout in millis...
 +
#define WDOG_TIMEOUT (long)7000
 +
// size of our incoming message buffer...
 +
#define MAXMSG 50
 +
 
 +
void setup() {
 +
  Serial.begin(9600);
 +
  pinMode(PIN_MODESW, INPUT);
 +
  pinMode(PIN_EYE2, OUTPUT);
 +
  pinMode(PIN_EYE1, OUTPUT);
 +
  pinMode(PIN_WDLED, OUTPUT);
 +
  //
 +
  pinMode(PIN_M1SPD, OUTPUT);
 +
  pinMode(PIN_M2SPD, OUTPUT);
 +
  pinMode(PIN_M1C1, OUTPUT);
 +
  pinMode(PIN_M1C2, OUTPUT);
 +
  pinMode(PIN_M2C1, OUTPUT);
 +
  pinMode(PIN_M2C2, OUTPUT);
 +
  // TODO soft serial link to MCU2
 +
  pinMode(PIN_MCU2TX, OUTPUT);
 +
  //
 +
  pinMode(PIN_BMP1, INPUT);
 +
  pinMode(PIN_BMP2, INPUT);
 +
   
 +
  // announce startup...
 +
  flasheyes(200);
 +
  Serial.println("\n=== CHEESOID ===");
 +
  // TODO: piezo buzzer to play a tune
 +
  mode = digitalRead(PIN_MODESW);
 +
 
 +
 
 +
  TestMotors();
 +
}
 +
 
 +
 
 +
void flasheyes(int zeds) {
 +
  for(int i=0;i<6;i++){
 +
    int onoff = i % 2;
 +
    digitalWrite(PIN_EYE2, onoff);
 +
    digitalWrite(PIN_EYE1, onoff);
 +
    delay(zeds);
 +
  }
 +
}
 +
 
 +
void loop() {
 +
  static int act;
 +
  do_switch();
 +
  do_eyes();
 +
  act = do_report();
 +
  act += do_serialinput();
 +
  if(!act)
 +
    delay(naptime);
 +
  loopcounter++;
 +
}
 +
 
 +
void do_switch() {
 +
  // button debounce: state must remain stable for N samples.
 +
  // This is just a lightweight debouncer
 +
  // used if we want to avoid the more general purpose
 +
  // but heavyweight Bounce library.
 +
 
 +
  const int debouncesamples = 5;
 +
  static int lastsample = 0;
 +
  static int steadycount = 0;
 +
  static int debounced = 0;
 +
  // if the value is steady for
 +
  int sample = digitalRead(PIN_MODESW);
 +
  // a change of value - restart the steady count...
 +
  if(sample != lastsample){
 +
    lastsample = sample;
 +
    steadycount = 0;
 +
    return;
 +
  }
 +
  steadycount++;
 +
  if(steadycount < debouncesamples)
 +
    return;
 +
  steadycount = debouncesamples;
 +
  debounced = sample;
 +
  // set the actual mode switch value
 +
  mode = debounced;
 +
}
 +
 
 +
void do_eyes() {
 +
  static int eyestate = 0;
 +
  if(loopcounter % eyefreq != 0)
 +
    return;
 +
  eyestate = !eyestate;
 +
  digitalWrite(PIN_EYE2, eyestate);
 +
  digitalWrite(PIN_EYE1, !eyestate);
 +
}
 +
 
 +
int do_report() {
 +
  if(loopcounter % reportfreq != 0)
 +
    return 0;
 +
  Serial.print("CHEESOID: mode = ");
 +
  Serial.print(mode);
 +
  //~ Serial.print(" | eye = ");
 +
  //~ Serial.print(onoff);
 +
  Serial.print(" | loop = ");
 +
  Serial.println(loopcounter);
 +
 
 +
  // also timeout the watchdog if set...
 +
  // this activity is limited by the reportfreq
 +
  if(wdoglast != 0){
 +
    long now = millis();
 +
    if(now - wdoglast > WDOG_TIMEOUT){
 +
      digitalWrite(PIN_WDLED, LOW);
 +
      counters[CTR_WDOGTO]++;
 +
      wdoglast = 0;
 +
    }
 +
  }
 +
  return 100;
 +
}
 +
 
 +
int do_serialinput() {
 +
  // can we avoid buffer overflows when using the
 +
  // standard serial library at a 10ms read rate at 9600 baud?
 +
  // Well, 9600 8-n-1 is 960 bytes per sec or 9.6 per 10ms
 +
  // The standard serial receive buffer holds 128 bytes so we're fine
 +
  // already nulled by compiler - we leave the final char alone!
 +
  static char msgbuf[MAXMSG+2];
 +
  static int len = 0;
 +
  // any data waiting?
 +
  int ba = Serial.available();
 +
  if(!ba)
 +
    return 0;
 +
  if(ba > counters[CTR_MAXREAD])
 +
    counters[CTR_MAXREAD] = ba;
 +
  for(int i = 0; i < ba; i++){
 +
    int c = Serial.read();
 +
    Serial.print("GOT:");
 +
    Serial.println(c, DEC);
 +
    // upon LF, process message...
 +
    if(c == 0x0A){
 +
      // NB: ignore incoming char - now looking at the buffer...
 +
      c = msgbuf[0];
 +
      if(c == 'E'){
 +
        flasheyes(50);
 +
        Serial.println("I OBEY: FLASH EYES!");
 +
        len = 0; // reset message
 +
        continue; // ignore rest of message!
 +
      }
 +
      // watchdog kick - proof that PC is alive
 +
      if(c == 'W'){
 +
        // LED on for a while (needs timeout)
 +
        // lastkick var
 +
        Serial.println("Watchdog!");
 +
        digitalWrite(PIN_WDLED, HIGH);
 +
        wdoglast = millis();
 +
        len = 0; // reset message
 +
        continue; // ignore rest of message!
 +
      }
 +
      // Drive command...
 +
      if(c == 'D'){
 +
        DriveInterpret(msgbuf, len);
 +
        len = 0; // reset message
 +
        continue;
 +
      }
 +
      // Test commands...
 +
      if(c == 'T'){
 +
        int testmode = msgbuf[1];
 +
        if(testmode == 'D') {
 +
            TestMotors();
 +
            len = 0;
 +
            continue;
 +
        }
 +
        len = 0; // reset message
 +
        continue;
 +
      }
 +
      counters[CTR_UNKMSG]++;
 +
      continue;
 +
    }
 +
    // check for and avoid overflow...
 +
    if(len > MAXMSG){
 +
      counters[CTR_OVERFLOW]++;
 +
      len = 0; // reset message
 +
      continue;
 +
    }
 +
    // add msg to end of buffer and carry on...
 +
    msgbuf[len] = c;
 +
    len++;
 +
    // null terminate for debugging - OK to do if buffer len is MAXMSG+2!
 +
    msgbuf[len] = '\0';
 +
  }
 +
  return ba;
 +
}
 +
 
 +
 
 +
 
 +
/*******************************************************************/
 +
/*                  Motor drive control messages                  */
 +
/*******************************************************************/
 +
//
 +
//~ D[L-dir][L-speed][R-dir][R-speed]
 +
//
 +
//~ direction = 1 char, 'F' = forwards, 'B' = backwards, 'X' = hold
 +
//~ speed = 3 ASCII decimal digits in range 000 to 255 left zero padded
 +
//
 +
// Each message is 9 chars long.
 +
//
 +
//~ Examples: -
 +
//~ DF255F255 = full speed ahead
 +
//~ DF127F127 = half speed ahead
 +
//~ DF000F000 = freewheel?
 +
//~ DX000X000 = hold stop
 +
//~ DB255F255 = fast rotate left
 +
//~ DF255B255 = fast rotate right
 +
//~ DX000F255 = fast pivot left
 +
//~ The PWM will continue unless stopped so we should have a timeout on MCU1
 +
/*******************************************************************/
 +
void DriveInterpret(const char* msg, int len)
 +
{
 +
  int pwm1, pwm2, motor1_c1, motor1_c2, motor2_c1, motor2_c2;
 +
  // Validate input
 +
  if(msg == 0 || len < 9 || msg[0] != 'D')
 +
      return;
 +
  pwm1 = ((msg[2]-'0')*100) + ((msg[3]-'0')*10) + (msg[4]-'0');
 +
  pwm2 = ((msg[6]-'0')*100) + ((msg[7]-'0')*10) + (msg[8]-'0');
 +
  // the motor directions are rather arbitrary as they
 +
  // can be easily wired as necessary
 +
  if(msg[1] == 'F'){
 +
    motor1_c1 = HIGH;
 +
    motor1_c2 = LOW;
 +
  } else if(msg[1] == 'B'){
 +
    motor1_c1 = LOW;
 +
    motor1_c2 = HIGH;
 +
  } else { // default to hold
 +
    motor1_c1 = LOW;
 +
    motor1_c2 = LOW;
 +
  }
 +
  if(msg[5] == 'F'){
 +
    motor2_c1 = HIGH;
 +
    motor2_c2 = LOW;
 +
  } else if(msg[5] == 'B'){
 +
    motor2_c1 = LOW;
 +
    motor2_c2 = HIGH;
 +
  } else { // default to hold
 +
    motor2_c1 = LOW;
 +
    motor2_c2 = LOW;
 +
  }
 +
  DriveMotors(motor1_c1, motor1_c2, pwm1, motor2_c1, motor2_c2, pwm2);
 +
}
 +
 
 +
void DriveMotors(int motor1_c1, int motor1_c2, int pwm1,
 +
    int motor2_c1, int motor2_c2, int pwm2)
 +
{
 +
    // TODO timeout and bumpers!
 +
    digitalWrite(PIN_M1C1, motor1_c1);
 +
    digitalWrite(PIN_M1C2, motor1_c2);
 +
    analogWrite(PIN_M1SPD, pwm1);
 +
    digitalWrite(PIN_M2C1, motor2_c1);
 +
    digitalWrite(PIN_M2C2, motor2_c2);
 +
    analogWrite(PIN_M2SPD, pwm2);
 +
}
 +
 
 +
// Test motors forever!
 +
void TestMotors()
 +
{
 +
#define INC_TESTS
 +
#ifdef INC_TESTS   
 +
   
 +
    char m[10];
 +
    int len = 9;
 +
    m[9] = '\0';
 +
    m[0] = 'D';
 +
   
 +
    for(;;){
 +
      Serial.println("MOTOR TEST");
 +
   
 +
        DriveInterpret("DF255F255", len);
 +
        delay(400);
 +
        DriveInterpret("DF255X255", len);
 +
        delay(400);
 +
        DriveInterpret("DX255F255", len);
 +
        delay(400);
 +
        DriveInterpret("DF128F128", len);
 +
        delay(400);
 +
        DriveInterpret("DF255B255", len);
 +
        delay(400);
 +
        DriveInterpret("DB255F255", len);
 +
        delay(400);
 +
        DriveInterpret("DF000F000", len);
 +
        delay(400);
 +
        DriveInterpret("DF100F100", len);
 +
        delay(400);
 +
        DriveInterpret("DF150F150", len);
 +
        delay(400);
 +
        DriveInterpret("DF200F200", len);
 +
        delay(400);
 +
        DriveInterpret("DX000X000", len);
 +
        delay(2000);
 +
    }
 +
 
 +
#endif /* INC_TESTS */   
 +
}
 +
 
 +
</syntaxhighlight>
 +
</div>
 +
 
 +
=== MCU2 software ===
 +
* drives LCD display
 +
* needs serial in from MCU1
 +
* status LED to panel
  
 
== eeePC mods ==
 
== eeePC mods ==
Line 306: Line 723:
 
</pre>
 
</pre>
 
</div>
 
</div>
 +
 +
Speaker volume
 +
* make commands for volume up and down
 +
* amixer set LineOut 3.20dB- unmute
 +
* amixer set LineOut 3.20dB+ unmute
 +
 +
  
 
=== eeePC problems ===
 
=== eeePC problems ===
Line 314: Line 738:
 
* firefox won't start - oh well!
 
* firefox won't start - oh well!
  
=== eeePC Software ===
+
== eeePC Software ==
 
* console read and process
 
* console read and process
 
* speech module
 
* speech module
Line 325: Line 749:
 
** mic input - and speech recognition
 
** mic input - and speech recognition
 
** GUI interface port and GUI app
 
** GUI interface port and GUI app
 +
 +
One annoyance is having to open the eeePC to find out its IP address to get back in via SSH. My proposed solution is to display the eeePC wlan IP address on the LCD on MCU2.
 +
<div style ="height:200px;overflow-x:hidden;overflow-y:auto;border: 4px solid green;">
 +
'''Get IP address to report on LCD'''
 +
<syntaxhighlight lang="java" line="GESHI_FANCY_LINE_NUMBERS" enclose="div">
 +
package com.tecspy.util;
 +
 +
import java.net.Inet6Address;
 +
import java.net.InetAddress;
 +
import java.net.NetworkInterface;
 +
import java.net.SocketException;
 +
import java.net.UnknownHostException;
 +
import java.util.Enumeration;
 +
 +
import org.apache.log4j.BasicConfigurator;
 +
import org.apache.log4j.Logger;
 +
 +
public class NetUtils {
 +
    static Logger log = Logger.getLogger(NetUtils.class);
 +
   
 +
    public static String getIps() {
 +
       
 +
        StringBuilder buf = new StringBuilder();
 +
        char div = '|';
 +
       
 +
        try {
 +
            InetAddress localHost = InetAddress.getLocalHost();
 +
            NetworkInterface ni = NetworkInterface.getByInetAddress(localHost);
 +
            Enumeration<InetAddress> ia = ni.getInetAddresses();
 +
            while (ia.hasMoreElements()) {
 +
                InetAddress el = ia.nextElement();
 +
                buf.append(div);
 +
                if (el instanceof Inet6Address) {
 +
                    buf.append("IPv6:");
 +
                } else {
 +
                    buf.append("IPv4:");
 +
                }
 +
                buf.append(" hostname:");
 +
                buf.append(el.getCanonicalHostName());
 +
                buf.append(" address:");
 +
                buf.append(el.getHostAddress());
 +
            }
 +
        } catch (NullPointerException e) {
 +
            log.error("Error: " + e.getMessage(), e);
 +
        } catch (SocketException e) {
 +
            log.error("Error: " + e.getMessage(), e);
 +
        } catch (UnknownHostException e) {
 +
            log.error("Error: " + e.getMessage(), e);
 +
        }
 +
        return buf.substring(1);
 +
    }
 +
   
 +
    /**
 +
    * @param args
 +
    */
 +
    public static void main(String[] args) {
 +
        BasicConfigurator.configure();
 +
        String ips = getIps();
 +
        log.info(ips);
 +
       
 +
    }
 +
   
 +
}
 +
</syntaxhighlight>
 +
</div>
 +
 +
Additionally I'll make use of some of my remote servers to track IP addresses posted by cheesoid...
 +
* In .bash_login run the checkservice script to start the cheesoid service
 +
* upon startup cheesoid gets its LAN IP address and uses wget to publish it
 +
* wget http://(sitename)/mick/cheesoid-track-wgetter?(wifi-lan-IP)
 +
** http://www.tecspy.com/cheesoid-status
  
 
= Additional =
 
= Additional =
Line 331: Line 826:
 
** http://robot-electronics.co.uk/acatalog/Ultrasonic_Rangers.html
 
** http://robot-electronics.co.uk/acatalog/Ultrasonic_Rangers.html
 
** http://www.robot-electronics.co.uk/htm/srf05tech.htm
 
** http://www.robot-electronics.co.uk/htm/srf05tech.htm
 +
* IR: SHARP GP2Y0A21YK
 +
** £10.50 from oomlout
 +
** http://www.oomlout.co.uk/infra-red-distance-sensor-w-cable-p-229.html
 +
 +
 +
* Ultrasonics have a wide beam (shape patterns usually in data sheets).
 +
* IR tends to have a narrow beam
 +
Wide beam in ultrasonic can help find smaller objects that the narrow if might miss if not in it direct line,
 +
IR can see wall but might miss table leg!
 +
--[[User:Lwk|&#39;RepRap&#39; Matt]] 07:48, 3 November 2011 (EST)
 +
 +
OK, I have a HC-SR04 which was only £2.38 from an ebay seller in China: -
 +
* info: http://letsmakerobots.com/node/30209
 +
* datasheet: http://jaktek.com/wp-content/uploads/2011/12/HC-SR04.pdf
 +
* seller: http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&item=320815133657#ht_3048wt_1398
 +
 +
<div style ="height:200px;overflow-x:hidden;overflow-y:auto;border: 4px solid green;">
 +
HC-SR04 Distance Measuring Transducer http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&item=320815133657#ht_3048wt_1398
 +
<pre>
 +
Product description
 +
Description:
 +
Module main technical parameters:
 +
1.Working Voltage : 5V(DC)
 +
2.Static current: Less than 2mA.
 +
3.Output signal: Electric frequency signal, high level 5V, low level 0V.
 +
4.Sensor angle: Not more than 15 degrees.
 +
5.Detection distance: 2cm-450cm.
 +
6.High precision: Up to 0.3cm
 +
7.Input trigger signal: 10us TTL impulse
 +
8.Echo signal : output TTL PWL signal
 +
Mode of connection:
 +
1.VCC
 +
2.trig(T)
 +
3.echo(R)
 +
4.GND
 +
Use method:
 +
Supply module with 5V, the output will be 5V while obstacle in range, or 0V if not.The out pin
 +
of this module is used as a switching output when anti-theft module, and without the feet when
 +
ranging modules.
 +
Note : the module should be inserted in the circuit before been power, which avoid producing
 +
high level of misoperation;if not, then power again.
 +
Module Working Principle:
 +
1.Adopt IO trigger through supplying at least 10us sequence of high level signal.
 +
2.The module automatically send eight 40khz square wave and automatically detect whether receive
 +
the returning pulse signal.
 +
3.If there is signals returning, through outputting high level and the time of high level
 +
continuing is the time of that from the ultrasonic transmitting to receiving. Test distance =
 +
(high level time * sound velocity (340M/S) / 2.
 +
Note : This module is not suitable to connect with electric power, if you need to connect this
 +
module with electronic power,then let the GND terminal of this module to be connected
 +
first,otherwise, it will affect the normal work of the module
 +
 +
 +
Package Include:
 +
 +
1 x New Arduino Ultrasonic Module HC-SR04 Distance Measuring Transducer Sensor</pre>
 +
</div>
 +
Awesome! It works! Here's my test sketch...
 +
<div style ="height:200px;overflow-x:hidden;overflow-y:auto;border: 4px solid green;">
 +
<syntaxhighlight lang="cpp" line="GESHI_FANCY_LINE_NUMBERS" enclose="div">
 +
 +
#include <Ultrasonic.h>
 +
 +
/*
 +
Ultrasonic range module HC-SR04
 +
*/
 +
 +
// Ultrasonic(int TrigPin, int EchoPin)
 +
Ultrasonic ultrasonic(8,7);
 +
 +
#define LEDPIN 13
 +
#define PIEZOPIN 9
 +
 +
void setup() {               
 +
  pinMode(LEDPIN, OUTPUT);
 +
  pinMode(PIEZOPIN, OUTPUT);
 +
  Serial.begin(9600); 
 +
}
 +
 +
void loop() {
 +
  checkInput();
 +
  digitalWrite(LEDPIN, HIGH);
 +
  delay(300);
 +
  checkInput();
 +
  digitalWrite(LEDPIN, LOW);
 +
  delay(300);
 +
}
 +
 +
void checkInput(){
 +
  long distance = ultrasonic.Ranging(CM);
 +
  Serial.print("ultrasonic: ");
 +
  Serial.print(distance); 
 +
  Serial.println(" cm");
 +
  if(distance < 200){
 +
    // nice pitch between 100 and 2000
 +
    int thisPitch = map(distance, 200, 2, 100, 2000);
 +
    tone(PIEZOPIN, thisPitch);
 +
    delay(100);
 +
    noTone(PIEZOPIN);
 +
  }
 +
}
 +
</syntaxhighlight >
 +
</div>
  
 
== Rotary Encoders for wheels ==
 
== Rotary Encoders for wheels ==
Line 338: Line 936:
 
* encoder usage in daylight
 
* encoder usage in daylight
 
* mounting encoder wheel to axle
 
* mounting encoder wheel to axle
 +
 +
== PIR - human sensor ==
 +
Using an AirWick FreshMatic...
 +
* http://letsmakerobots.com/node/26618
 +
* http://outroot.com/blog/2010/01/10/airwick-motion-sensor-pir-with-an-arduino/
 +
* http://blog.outroot.com/airwick-motion-sensor-pir-with-an-arduino/
 +
 +
Original author's sketch...
 +
<div style ="height:200px;overflow-x:hidden;overflow-y:auto;border: 4px solid green;">
 +
<syntaxhighlight lang="cpp" line="GESHI_FANCY_LINE_NUMBERS">
 +
// Code from http://www.arduino.cc/playground/Code/PIRsense
 +
// Modifed by Kevin Darlington for an Airwick Freshmatic PIR.
 +
 +
//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
 +
int calibrationTime = 30;
 +
 +
//the time when the sensor outputs a low impulse
 +
long unsigned int lowIn;
 +
 +
//the amount of milliseconds the sensor has to be low
 +
//before we assume all motion has stopped
 +
long unsigned int pause = 1000;
 +
 +
// The threshold. The sensor value has to go under this
 +
// for motion to be present.
 +
int threshold = 100;
 +
 +
boolean lockLow = true;
 +
boolean takeLowTime;
 +
 +
int pirPin = 0; // analog pin 0
 +
int ledPin = 12;
 +
 +
void setup()
 +
{
 +
  Serial.begin(9600);
 +
 +
  // This doesn't seem to make a bit of difference. But it's
 +
  // here anyways.
 +
  Serial.print("calibrating sensor ");
 +
  for (int i = 0; i < calibrationTime; i++) {
 +
    Serial.print(".");
 +
    delay(1000);
 +
  }
 +
  Serial.println(" done");
 +
  Serial.println("SENSOR ACTIVE");
 +
  delay(50);
 +
}
 +
 +
void loop()
 +
{
 +
  if (analogRead(0) < threshold) {
 +
    if (lockLow) {
 +
      // Turn on the LED by setting the pin to OUTPUT and LOW
 +
      pinMode(ledPin, OUTPUT);
 +
      digitalWrite(ledPin, LOW);
 +
 +
      //makes sure we wait for a transition to LOW before any further output is made:
 +
      lockLow = false;
 +
      Serial.println("---");
 +
      Serial.print("motion detected at ");
 +
      Serial.print(millis()/1000);
 +
      Serial.println(" sec");
 +
      delay(50);
 +
    }
 +
    takeLowTime = true;
 +
  }
 +
 +
  if (analogRead(0) > threshold) {
 +
 +
    if (takeLowTime) {
 +
      lowIn = millis();          //save the time of the transition from high to LOW
 +
      takeLowTime = false;      //make sure this is only done at the start of a LOW phase
 +
    }
 +
    //if the sensor is low for more than the given pause,
 +
    //we assume that no more motion is going to happen
 +
    if (!lockLow && millis() - lowIn > pause) {
 +
      // Turn off the LED by setting the pint to INPUT
 +
      pinMode(12, INPUT);
 +
 +
      //makes sure this block of code is only executed again after
 +
      //a new motion sequence has been detected
 +
      lockLow = true;
 +
      Serial.print("motion ended at ");      //output
 +
      Serial.print((millis() - pause)/1000);
 +
      Serial.println(" sec");
 +
      delay(50);
 +
    }
 +
  }
 +
}
 +
</syntaxhighlight>
 +
</div>
 +
 +
My version "Smart Motion A-06" "Ver:RB-S04" "2008/08/05"
 +
 +
Nice usage...
 +
https://picasaweb.google.com/patyoungers/MunnyDollNightlightInfraredDetector?authkey=Gv1sRgCK7Ao_e2lZrfsgE#
 +
 +
Original PIRsense: http://www.arduino.cc/playground/Code/PIRsense
 +
 +
== Weight ==
 +
 +
As of today (2nd April 2012) the component weights are as follows
 +
* 1665g = baseplate, hubs, wheels, motors, alu eeePC bracket
 +
* 1246g = eeePC w. battery and holding box
 +
* 294g = motor batteries 12x AA, 3x cases
 +
* 2216g = cheesoid body: cardboard cylinder, eyes, top and side panels, LCD display, mode switch, keyswitch and power buttons
 +
 +
Total weight 5421g!!!!!!!
 +
 +
==Details==
 +
These details would be better on a subpage rather than a talkpage. [[User:Jon|Jon]] ([[User talk:Jon|talk]]) 21:20, 24 April 2019 (UTC)

Latest revision as of 21:20, 24 April 2019

Interaction Goals

Cheesoid is intended to be a fully autonomous mobile robot that interacts with people and objects in its environment. Human-Robot interaction is a massive subject but I intend to get some rudimentary (and comedic) speech and general input features in place as soon as possible. The environment interactions will develop as I learn more about sensing and mapping. Since the robot will live at Hackspace I have two basic goals for comedy value: -

  • interact with the fridge for cheese status
  • interact with the petrol pump for petrol status

So...

  • it needs to know where they are to go and talk to them
  • the status needs to be stored and needs to be set - Xino at each? Via IRC bot? Some web interface?
  • it need to be able to read the status from the fridge and the petrol pump - what sort of interface? IR remote control?

Petrol status

  • just a number!

Cheese status

  • many cheese types - each with use by date
  • Primula status is "in tube"

Human - Robot Interactions: speech

With the eeePC I have a very capable processor on board and I'm taking full advantage of that: -

  • using espeak for text to speech
  • using sphinx for speech recognition

Text to Speech with espeak

http://espeak.sourceforge.net/

The espeak voices aren't really robotic enough but can be made more so by creating a custom voice

speech recognition with sphinx

http://cmusphinx.sourceforge.net/wiki/tutorialconcepts

The sphinx packages

p    --\ sphinx2-bin                                                                                                                    <none>     0.6-2.1
  Description: speech recognition utilities
    Sphinx 2 is a real-time, speaker-independent speech recognition system.

    This package contains examples and utilities that use Sphinx. It also includes a sample language model that is capable of recognizing simple commands
    like "go forward ten meters" and other commands one might use to tell a robot where to move.
  Priority: optional
  Section: universe/sound
  Maintainer: Ubuntu MOTU Developers <ubuntu-motu@lists.ubuntu.com>
  Compressed size: 129k
  Uncompressed size: 500k
  Source Package: sphinx2
  --\ Depends (3)
    --- libc6 (>= 2.4)
    --- libsphinx2g0 (>= 0.6) (UNSATISFIED)
    --- sphinx2-hmm-6k (UNSATISFIED)
  --\ Packages which depend on sphinx2-bin (0)
  --\ Versions of sphinx2-bin (1)
p    0.6-2.1

Good examples of the use of sphinx: -

If Sphinx 4 works on the eeePC (try the demos) then go ahead and use it from Java.

Conversations and dialogue

In order to have a meaningful (preferably amusing and slightly uncanny) interaction with humans there needs to be a dialogue and some involvement of non-verbal communications: perhaps some shared experience, empathy, etc. We can fake a lot of things to push the human participant closer to the goal!

  • New status phrases (with equivalents and random or circular selection) - use emotional terms
    • "I don't know where I am"
    • "I'm not happy"
    • "I'm scared"
    • "I'm upset"
    • "this is all wrong" - "I don't like this situation"
    • "what is this place?"
    • "where am I? I can't see and I can't move"
    • "I haven't been here before"
    • "I need to check the petrol but I can't get there"
    • "I need to check the cheese but I can't get there"
    • "I can't move. Someone will have to carry me"
    • "I'm very lonely. Can somebody talk to me" (on IRC/or actual voice)
    • "There's nothing here"
    • "This is terrible. Is there no god?"
  • Wake up phrases for Map module...
    • "Do I know this place?"
    • "Is this [location name]?"
    • "Can somebody tell me where I am?"

Mobility

  • motors
  • wheels
  • chassis
  • motor power
  • motor control circuitry
  • big battery

Drive motors: I now have my 12V 150RPM gearmotors from China


OK, motors tested with a two motor circuit...

Motor Test sketch (non-PWM)

  1 int motor_left[] = {
  2   2, 3};
  3 int motor_right[] = {
  4   7, 8};
  5 const int ledPin = 13;      // LED 
  6 const int switchPin = 10;    // switch input
  7 const int enablePin = 9;    // H-bridge enable pin
  8 
  9 
 10 // ————————————————————————— Setup
 11 void setup() {
 12   Serial.begin(9600);
 13   pinMode(ledPin, OUTPUT);
 14   pinMode(switchPin, INPUT); 
 15   pinMode(enablePin, OUTPUT);
 16 
 17 
 18   // Setup motors
 19   int i;
 20   for(i = 0; i < 2; i++){
 21     pinMode(motor_left[i], OUTPUT);
 22     pinMode(motor_right[i], OUTPUT);
 23   }
 24   check_enable();
 25   // blink the LED 3 times. This should happen only once.
 26   // if you see the LED blink three times, it means that the module
 27   // reset itself,. probably because the motor caused a brownout
 28   // or a short.
 29   blink(ledPin, 3, 100);
 30 
 31 }
 32 
 33 
 34 void check_enable(){
 35   digitalWrite(enablePin, digitalRead(switchPin));
 36 }
 37 // ————————————————————————— Loop
 38 void loop() {
 39 
 40   drive_forward();
 41   delay(1000);
 42   motor_stop();
 43   Serial.println("1");
 44 
 45   drive_backward();
 46   delay(1000);
 47   motor_stop();
 48   Serial.println("2");
 49 
 50   turn_left();
 51   delay(1000);
 52   motor_stop();
 53   Serial.println("3");
 54 
 55   turn_right();
 56   delay(1000);
 57   motor_stop();
 58   Serial.println("4");
 59 
 60   motor_stop();
 61   delay(1000);
 62   motor_stop();
 63   Serial.println("5");
 64 }
 65 
 66 // ————————————————————————— Drive
 67 
 68 void motor_stop(){
 69   check_enable();
 70   digitalWrite(motor_left[0], LOW);
 71   digitalWrite(motor_left[1], LOW);
 72 
 73   digitalWrite(motor_right[0], LOW);
 74   digitalWrite(motor_right[1], LOW);
 75   delay(25);
 76 }
 77 
 78 void drive_forward(){
 79   check_enable();
 80   digitalWrite(motor_left[0], HIGH);
 81   digitalWrite(motor_left[1], LOW);
 82 
 83   digitalWrite(motor_right[0], HIGH);
 84   digitalWrite(motor_right[1], LOW);
 85 }
 86 
 87 void drive_backward(){
 88   check_enable();
 89   digitalWrite(motor_left[0], LOW);
 90   digitalWrite(motor_left[1], HIGH);
 91 
 92   digitalWrite(motor_right[0], LOW);
 93   digitalWrite(motor_right[1], HIGH);
 94 }
 95 
 96 void turn_left(){
 97   check_enable();
 98   digitalWrite(motor_left[0], LOW);
 99   digitalWrite(motor_left[1], HIGH);
100 
101   digitalWrite(motor_right[0], HIGH);
102   digitalWrite(motor_right[1], LOW);
103 }
104 
105 void turn_right(){
106   check_enable();
107   digitalWrite(motor_left[0], HIGH);
108   digitalWrite(motor_left[1], LOW);
109 
110   digitalWrite(motor_right[0], LOW);
111   digitalWrite(motor_right[1], HIGH);
112 }
113 
114 /*
115     blinks an LED
116  */
117 void blink(int whatPin, int howManyTimes, int milliSecs) {
118   int i = 0;
119   for ( i = 0; i < howManyTimes; i++) {
120     digitalWrite(whatPin, HIGH);
121     delay(milliSecs/2);
122     digitalWrite(whatPin, LOW);
123     delay(milliSecs/2);
124   }
125 }

This sketch is a munge of http://letsmakerobots.com/node/2074 and http://itp.nyu.edu/physcomp/Labs/DCMotorControl

OK, after a couple of nights at Hackspace I now have the motors mounted and coupled to the wheel assemblies but the whole thing is way too weak to drive the wheels when using the big 12v lead-acid batteries I have. It's a little better with the bench power supply at 12v and it's how I'd want it with the bench supply at 14v. I think there's multiple problems here: the wheel assemblies are heavy and the shed-built motor brackets are inaccurate, as are the polymorph couplings (an idea borrowed from here http://letsmakerobots.com/node/15354). I thought that the wheel assembly would be necessary to take some of the load off the motor axles but I see quite a lot of setups with the wheels attached directly to the motors as in this example that also uses 38mm motors: http://www.pololu.com/catalog/product/730

I'm tempted to redesign around some direct wheel couplings and purchased wheels even though I love the CD wheel idea.

I've joined the letsmakerobots.com forum and I'm going to ask some advice there.

--Michael Erskine 09:18, 8 October 2011 (EST)

MCU1 Motor Control

MCU1 will drive each motor via the SN754410 with three signals: Motor_Logic_1, Motor_Logic_2, and Motor_Enable (corresponding to the SN754410 pins). The Motor_Enable signals will be PWM thus using in total 2 PWM outputs plus 4 digital outputs. This allows a simple interface to the SN754410.

Control is via serial from the eeePC giving parameters of direction and speed for both motors at once. The motors can be put in hold with a stop command. The messages need to be quick for MCU1 to read and interpret but also easily human-readable.

 D[L-dir][L-speed][R-dir][R-speed]
 
 direction = 1 char, 'F' = forwards, 'B' = backwards, 'X' = hold
 speed = 3 ASCII decimal digits in range 000 to 255 left zero padded 

Examples: -

  • DF255F255 = full speed ahead
  • DF127F127 = half speed ahead
  • DF000F000 = freewheel?
  • DX000X000 = hold stop
  • DB255F255 = fast rotate left
  • DF255B255 = fast rotate right
  • DX000F255 = fast pivot left

The PWM will continue unless stopped so we should have a timeout on MCU1

Bodywork

  • battery regulation: http://letsmakerobots.com/node/3880
  • battery mounting - where?
    • chassis strong enough to carry those batteries? !!!!
    • aluminium cross bracing?
    • perhaps move to 2x 6V - keep it flexible
  • cylinder case mods
  • pizza box - vertical holder

Motor shaft couplings being the most annoying thing right now

  • I really need some well made couplings like these

Small hose clips may be the thing.

Brains

The current design makes use of a few processors: the small cheap eeePC 701 4G netbook and a couple of inexpensive (£7) Xino Arduino micros.

MCU1 and MCU2

Xino devices

MCU1 PIN usage summary table here on Google Docs

MCU1 software

  1 /*******************************************************************/
  2 /*                          CHEESOID MCU1                          */
  3 /*******************************************************************/
  4 
  5 // I/O Pin definitions...
  6 // The mode switch is on a digital input...
  7 const int PIN_MODESW = 4;
  8 // Two LEDs are used for eyes on two digital pins...
  9 const int PIN_EYE1 = 7;
 10 const int PIN_EYE2 = 8;
 11 // Built-in LED on pin 13 used for watchdog status...
 12 const int PIN_WDLED = 13;
 13 // Motor enable/speed PWM outputs...
 14 const int PIN_M1SPD = 9;
 15 const int PIN_M2SPD = 10;
 16 // Motor control logic digital outputs...
 17 const int PIN_M1C1 = 14;
 18 const int PIN_M1C2 = 15;
 19 const int PIN_M2C1 = 16;
 20 const int PIN_M2C2 = 17;
 21 // Bumper inputs...
 22 const int PIN_BMP1 = 18;
 23 const int PIN_BMP2 = 19;
 24 // Soft serial to MCU2...
 25 const int PIN_MCU2TX = 11;
 26 
 27 
 28 // Last watchdog time tracking...
 29 unsigned long wdoglast = 0;
 30 // main loop has 10ms sleeps...
 31 const int naptime = 10;
 32 // the eyes flash every 60 loops = 600ms...
 33 int eyefreq = 60;
 34 // info is sent out on the serial port every 90 loops = 900ms...
 35 int reportfreq = 90;
 36 // a simple loop counter is used to decide what to do...
 37 unsigned int loopcounter = 0;
 38 // mode switch state...
 39 int mode = 0;
 40 
 41 // System counters...
 42 // Max number of bytes read in one go...
 43 #define CTR_MAXREAD 0
 44 // number of buffer overflows avoided...
 45 #define CTR_OVERFLOW 1
 46 // Number of bytes received...
 47 #define CTR_BYTES 2
 48 // Number of unknown message commands...
 49 #define CTR_UNKMSG 3
 50 // Number of unknown message commands...
 51 #define CTR_WDOGTO 4
 52 static int counters[4];
 53 // watchdog timout in millis...
 54 #define WDOG_TIMEOUT (long)7000
 55 // size of our incoming message buffer...
 56 #define MAXMSG 50
 57 
 58 void setup() {
 59   Serial.begin(9600);
 60   pinMode(PIN_MODESW, INPUT);
 61   pinMode(PIN_EYE2, OUTPUT);
 62   pinMode(PIN_EYE1, OUTPUT);
 63   pinMode(PIN_WDLED, OUTPUT);
 64   //
 65   pinMode(PIN_M1SPD, OUTPUT);
 66   pinMode(PIN_M2SPD, OUTPUT);
 67   pinMode(PIN_M1C1, OUTPUT);
 68   pinMode(PIN_M1C2, OUTPUT);
 69   pinMode(PIN_M2C1, OUTPUT);
 70   pinMode(PIN_M2C2, OUTPUT);
 71   // TODO soft serial link to MCU2
 72   pinMode(PIN_MCU2TX, OUTPUT);
 73   //
 74   pinMode(PIN_BMP1, INPUT);
 75   pinMode(PIN_BMP2, INPUT);
 76     
 77   // announce startup...
 78   flasheyes(200);
 79   Serial.println("\n=== CHEESOID ===");
 80   // TODO: piezo buzzer to play a tune
 81   mode = digitalRead(PIN_MODESW);
 82   
 83   
 84   TestMotors();
 85 }
 86 
 87   
 88 void flasheyes(int zeds) {
 89   for(int i=0;i<6;i++){
 90     int onoff = i % 2;
 91     digitalWrite(PIN_EYE2, onoff);
 92     digitalWrite(PIN_EYE1, onoff);
 93     delay(zeds);
 94   }
 95 }
 96 
 97 void loop() {
 98   static int act;
 99   do_switch();
100   do_eyes();
101   act = do_report();
102   act += do_serialinput();
103   if(!act)
104     delay(naptime);
105   loopcounter++;
106 }
107 
108 void do_switch() {
109   // button debounce: state must remain stable for N samples.
110   // This is just a lightweight debouncer
111   // used if we want to avoid the more general purpose 
112   // but heavyweight Bounce library.
113 
114   const int debouncesamples = 5;
115   static int lastsample = 0;
116   static int steadycount = 0;
117   static int debounced = 0;
118   // if the value is steady for 
119   int sample = digitalRead(PIN_MODESW);
120   // a change of value - restart the steady count...
121   if(sample != lastsample){
122     lastsample = sample;
123     steadycount = 0;
124     return;
125   }
126   steadycount++;
127   if(steadycount < debouncesamples)
128     return;
129   steadycount = debouncesamples;
130   debounced = sample;
131   // set the actual mode switch value
132   mode = debounced;
133 }
134 
135 void do_eyes() {
136   static int eyestate = 0;
137   if(loopcounter % eyefreq != 0)
138     return;
139   eyestate = !eyestate;
140   digitalWrite(PIN_EYE2, eyestate);
141   digitalWrite(PIN_EYE1, !eyestate);
142 }
143 
144 int do_report() {
145   if(loopcounter % reportfreq != 0)
146     return 0;
147   Serial.print("CHEESOID: mode = ");
148   Serial.print(mode);
149   //~ Serial.print(" | eye = ");
150   //~ Serial.print(onoff);
151   Serial.print(" | loop = ");
152   Serial.println(loopcounter);
153 
154   // also timeout the watchdog if set...
155   // this activity is limited by the reportfreq
156   if(wdoglast != 0){
157     long now = millis();
158     if(now - wdoglast > WDOG_TIMEOUT){
159       digitalWrite(PIN_WDLED, LOW);
160       counters[CTR_WDOGTO]++;
161       wdoglast = 0;
162     }
163   }
164   return 100;
165 }
166 
167 int do_serialinput() {
168   // can we avoid buffer overflows when using the 
169   // standard serial library at a 10ms read rate at 9600 baud?
170   // Well, 9600 8-n-1 is 960 bytes per sec or 9.6 per 10ms
171   // The standard serial receive buffer holds 128 bytes so we're fine
172   // already nulled by compiler - we leave the final char alone!
173   static char msgbuf[MAXMSG+2];
174   static int len = 0;
175   // any data waiting?
176   int ba = Serial.available();
177   if(!ba)
178     return 0;
179   if(ba > counters[CTR_MAXREAD])
180     counters[CTR_MAXREAD] = ba;
181   for(int i = 0; i < ba; i++){
182     int c = Serial.read();
183     Serial.print("GOT:");
184     Serial.println(c, DEC);
185     // upon LF, process message...
186     if(c == 0x0A){
187       // NB: ignore incoming char - now looking at the buffer...
188       c = msgbuf[0];
189       if(c == 'E'){
190         flasheyes(50);
191         Serial.println("I OBEY: FLASH EYES!");
192         len = 0; // reset message
193         continue; // ignore rest of message!
194       }
195       // watchdog kick - proof that PC is alive
196       if(c == 'W'){
197         // LED on for a while (needs timeout)
198         // lastkick var
199         Serial.println("Watchdog!");
200         digitalWrite(PIN_WDLED, HIGH);
201         wdoglast = millis();
202         len = 0; // reset message
203         continue; // ignore rest of message!
204       }
205       // Drive command...
206       if(c == 'D'){
207         DriveInterpret(msgbuf, len);
208         len = 0; // reset message
209         continue;
210       }
211       // Test commands...
212       if(c == 'T'){
213         int testmode = msgbuf[1];
214         if(testmode == 'D') {
215             TestMotors();
216             len = 0;
217             continue;
218         }
219         len = 0; // reset message
220         continue;
221       }
222       counters[CTR_UNKMSG]++;
223       continue;
224     }
225     // check for and avoid overflow...
226     if(len > MAXMSG){
227       counters[CTR_OVERFLOW]++;
228       len = 0; // reset message
229       continue;
230     }
231     // add msg to end of buffer and carry on...
232     msgbuf[len] = c;
233     len++;
234     // null terminate for debugging - OK to do if buffer len is MAXMSG+2!
235     msgbuf[len] = '\0';
236   }
237   return ba;
238 }
239 
240 
241 
242 /*******************************************************************/
243 /*                  Motor drive control messages                   */
244 /*******************************************************************/
245 // 
246 //~ D[L-dir][L-speed][R-dir][R-speed]
247 //
248 //~ direction = 1 char, 'F' = forwards, 'B' = backwards, 'X' = hold
249 //~ speed = 3 ASCII decimal digits in range 000 to 255 left zero padded 
250 // 
251 // Each message is 9 chars long.
252 //
253 //~ Examples: -
254 //~ DF255F255 = full speed ahead
255 //~ DF127F127 = half speed ahead
256 //~ DF000F000 = freewheel?
257 //~ DX000X000 = hold stop
258 //~ DB255F255 = fast rotate left
259 //~ DF255B255 = fast rotate right
260 //~ DX000F255 = fast pivot left
261 //~ The PWM will continue unless stopped so we should have a timeout on MCU1
262 /*******************************************************************/
263 void DriveInterpret(const char* msg, int len)
264 {
265   int pwm1, pwm2, motor1_c1, motor1_c2, motor2_c1, motor2_c2;
266   // Validate input
267   if(msg == 0 || len < 9 || msg[0] != 'D')
268       return;
269   pwm1 = ((msg[2]-'0')*100) + ((msg[3]-'0')*10) + (msg[4]-'0');
270   pwm2 = ((msg[6]-'0')*100) + ((msg[7]-'0')*10) + (msg[8]-'0');
271   // the motor directions are rather arbitrary as they 
272   // can be easily wired as necessary
273   if(msg[1] == 'F'){
274     motor1_c1 = HIGH;
275     motor1_c2 = LOW;
276   } else if(msg[1] == 'B'){
277     motor1_c1 = LOW;
278     motor1_c2 = HIGH;
279   } else { // default to hold
280     motor1_c1 = LOW;
281     motor1_c2 = LOW;
282   }
283   if(msg[5] == 'F'){
284     motor2_c1 = HIGH;
285     motor2_c2 = LOW;
286   } else if(msg[5] == 'B'){
287     motor2_c1 = LOW;
288     motor2_c2 = HIGH;
289   } else { // default to hold
290     motor2_c1 = LOW;
291     motor2_c2 = LOW;
292   }
293   DriveMotors(motor1_c1, motor1_c2, pwm1, motor2_c1, motor2_c2, pwm2);
294 }
295 
296 void DriveMotors(int motor1_c1, int motor1_c2, int pwm1, 
297     int motor2_c1, int motor2_c2, int pwm2)
298 {
299     // TODO timeout and bumpers!
300     digitalWrite(PIN_M1C1, motor1_c1);
301     digitalWrite(PIN_M1C2, motor1_c2);
302     analogWrite(PIN_M1SPD, pwm1);
303     digitalWrite(PIN_M2C1, motor2_c1);
304     digitalWrite(PIN_M2C2, motor2_c2);
305     analogWrite(PIN_M2SPD, pwm2);
306 }
307 
308 // Test motors forever!
309 void TestMotors()
310 {
311 #define INC_TESTS
312 #ifdef INC_TESTS    
313     
314     char m[10];
315     int len = 9;
316     m[9] = '\0';
317     m[0] = 'D';
318     
319     for(;;){
320       Serial.println("MOTOR TEST");
321     
322         DriveInterpret("DF255F255", len);
323         delay(400);
324         DriveInterpret("DF255X255", len);
325         delay(400);
326         DriveInterpret("DX255F255", len);
327         delay(400);
328         DriveInterpret("DF128F128", len);
329         delay(400);
330         DriveInterpret("DF255B255", len);
331         delay(400);
332         DriveInterpret("DB255F255", len);
333         delay(400);
334         DriveInterpret("DF000F000", len);
335         delay(400);
336         DriveInterpret("DF100F100", len);
337         delay(400);
338         DriveInterpret("DF150F150", len);
339         delay(400);
340         DriveInterpret("DF200F200", len);
341         delay(400);
342         DriveInterpret("DX000X000", len);
343         delay(2000);
344     }
345 
346 #endif /* INC_TESTS */    
347 }

MCU2 software

  • drives LCD display
  • needs serial in from MCU1
  • status LED to panel

eeePC mods

Hardware and system mods to support "isolated usage".

  • soldered in an external power button cable
    • PWR button sub-assembly with safety keyswitch - mount on side panel
    • TODO LED in "Micro" and other nice illuminated buttons
    • Monostable/bistable startup flasher circuit for "Micro" switch?
  • "pizza box" container
    • power port extension
    • USB extension - USB hub - still powering MCU1 from USB - much drain?
#!/bin/sh
LID_STATE=`cat /proc/acpi/button/lid/LID/state | awk '{print $2 }'`

if [ $LID_STATE = "closed" ] ; then
#    /etc/acpi/suspend2ram.sh
        /bin/su user -c "/usr/bin/xrandr --output VGA --mode 800x600 --output LVDS --off"
fi
if [ $LID_STATE = "open" ] ; then
        /bin/su user -c "/usr/bin/xrandr --output LVDS --preferred --output VGA --off"
fi
exit 0

This is not enough: the eeePC will not power on with the lid closed so I had to disable the lid closed sensor by removing the magnet from screen section of the case

# minimal brightness
echo 0 > /sys/devices/platform/eeepc/backlight/eeepc/brightness"
# screen off after 2 minutes
xset dpms 0 0 120

Speaker volume

  • make commands for volume up and down
  • amixer set LineOut 3.20dB- unmute
  • amixer set LineOut 3.20dB+ unmute


eeePC problems

  • unionfs inode depletion causing "No space left on device" but df shows plenty of space!
  • running out of space due to other errors ~/.Xsession-errors
  • firefox won't start - oh well!

eeePC Software

  • console read and process
  • speech module
    • speech commands from stdin
    • speech thread - busy flag and job queue management
  • motor module
    • motor control input from STDIN
  • sensor module
    • camera module - look at Java interaction with V4L or whatever is in use
    • mic input - and speech recognition
    • GUI interface port and GUI app

One annoyance is having to open the eeePC to find out its IP address to get back in via SSH. My proposed solution is to display the eeePC wlan IP address on the LCD on MCU2.

Get IP address to report on LCD

 1 package com.tecspy.util;
 2 
 3 import java.net.Inet6Address;
 4 import java.net.InetAddress;
 5 import java.net.NetworkInterface;
 6 import java.net.SocketException;
 7 import java.net.UnknownHostException;
 8 import java.util.Enumeration;
 9 
10 import org.apache.log4j.BasicConfigurator;
11 import org.apache.log4j.Logger;
12 
13 public class NetUtils {
14     static Logger log = Logger.getLogger(NetUtils.class);
15     
16     public static String getIps() {
17         
18         StringBuilder buf = new StringBuilder();
19         char div = '|';
20         
21         try {
22             InetAddress localHost = InetAddress.getLocalHost();
23             NetworkInterface ni = NetworkInterface.getByInetAddress(localHost);
24             Enumeration<InetAddress> ia = ni.getInetAddresses();
25             while (ia.hasMoreElements()) {
26                 InetAddress el = ia.nextElement();
27                 buf.append(div);
28                 if (el instanceof Inet6Address) {
29                     buf.append("IPv6:");
30                 } else {
31                     buf.append("IPv4:");
32                 }
33                 buf.append(" hostname:");
34                 buf.append(el.getCanonicalHostName());
35                 buf.append(" address:");
36                 buf.append(el.getHostAddress());
37             }
38         } catch (NullPointerException e) {
39             log.error("Error: " + e.getMessage(), e);
40         } catch (SocketException e) {
41             log.error("Error: " + e.getMessage(), e);
42         } catch (UnknownHostException e) {
43             log.error("Error: " + e.getMessage(), e);
44         }
45         return buf.substring(1);
46     }
47     
48     /**
49      * @param args
50      */
51     public static void main(String[] args) {
52         BasicConfigurator.configure();
53         String ips = getIps();
54         log.info(ips);
55         
56     }
57     
58 }

Additionally I'll make use of some of my remote servers to track IP addresses posted by cheesoid...

Additional

Range Sensors


  • Ultrasonics have a wide beam (shape patterns usually in data sheets).
  • IR tends to have a narrow beam

Wide beam in ultrasonic can help find smaller objects that the narrow if might miss if not in it direct line, IR can see wall but might miss table leg! --'RepRap' Matt 07:48, 3 November 2011 (EST)

OK, I have a HC-SR04 which was only £2.38 from an ebay seller in China: -

HC-SR04 Distance Measuring Transducer http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&item=320815133657#ht_3048wt_1398

Product description
Description:
Module main technical parameters:
1.Working Voltage : 5V(DC)
2.Static current: Less than 2mA.
3.Output signal: Electric frequency signal, high level 5V, low level 0V.
4.Sensor angle: Not more than 15 degrees.
5.Detection distance: 2cm-450cm.
6.High precision: Up to 0.3cm 
7.Input trigger signal: 10us TTL impulse
8.Echo signal : output TTL PWL signal
Mode of connection:
1.VCC
2.trig(T)
3.echo(R)
4.GND
Use method:
Supply module with 5V, the output will be 5V while obstacle in range, or 0V if not.The out pin 
of this module is used as a switching output when anti-theft module, and without the feet when 
ranging modules.
Note : the module should be inserted in the circuit before been power, which avoid producing 
high level of misoperation;if not, then power again. 
Module Working Principle: 
1.Adopt IO trigger through supplying at least 10us sequence of high level signal.
2.The module automatically send eight 40khz square wave and automatically detect whether receive 
the returning pulse signal.
3.If there is signals returning, through outputting high level and the time of high level 
continuing is the time of that from the ultrasonic transmitting to receiving. Test distance = 
(high level time * sound velocity (340M/S) / 2.
Note : This module is not suitable to connect with electric power, if you need to connect this 
module with electronic power,then let the GND terminal of this module to be connected 
first,otherwise, it will affect the normal work of the module


Package Include:

1 x New Arduino Ultrasonic Module HC-SR04 Distance Measuring Transducer Sensor

Awesome! It works! Here's my test sketch...

 1 #include <Ultrasonic.h>
 2 
 3 /*
 4 Ultrasonic range module HC-SR04
 5  */
 6 
 7 // Ultrasonic(int TrigPin, int EchoPin)
 8 Ultrasonic ultrasonic(8,7);
 9 
10 #define LEDPIN 13
11 #define PIEZOPIN 9
12 
13 void setup() {                
14   pinMode(LEDPIN, OUTPUT);
15   pinMode(PIEZOPIN, OUTPUT);
16   Serial.begin(9600);   
17 }
18 
19 void loop() {
20   checkInput();
21   digitalWrite(LEDPIN, HIGH);
22   delay(300);
23   checkInput();
24   digitalWrite(LEDPIN, LOW);
25   delay(300);
26 }
27 
28 void checkInput(){
29   long distance = ultrasonic.Ranging(CM);
30   Serial.print("ultrasonic: ");
31   Serial.print(distance);  
32   Serial.println(" cm");
33   if(distance < 200){
34     // nice pitch between 100 and 2000
35     int thisPitch = map(distance, 200, 2, 100, 2000);
36     tone(PIEZOPIN, thisPitch);
37     delay(100);
38     noTone(PIEZOPIN);
39   }
40 }

Rotary Encoders for wheels

  • an easily available "obsolete" ball type PS/2 mouse
  • Microsoft "Mouse Port Compatible Mouse 2.0A"
  • using the serial output from the mouse circuitry
  • encoder usage in daylight
  • mounting encoder wheel to axle

PIR - human sensor

Using an AirWick FreshMatic...

Original author's sketch...

 1 // Code from http://www.arduino.cc/playground/Code/PIRsense
 2 // Modifed by Kevin Darlington for an Airwick Freshmatic PIR.
 3 
 4 //the time we give the sensor to calibrate (10-60 secs according to the datasheet)
 5 int calibrationTime = 30; 
 6 
 7 //the time when the sensor outputs a low impulse
 8 long unsigned int lowIn;
 9 
10 //the amount of milliseconds the sensor has to be low
11 //before we assume all motion has stopped
12 long unsigned int pause = 1000;
13 
14 // The threshold. The sensor value has to go under this
15 // for motion to be present.
16 int threshold = 100;
17 
18 boolean lockLow = true;
19 boolean takeLowTime;
20 
21 int pirPin = 0; // analog pin 0
22 int ledPin = 12;
23 
24 void setup()
25 {
26   Serial.begin(9600);
27 
28   // This doesn't seem to make a bit of difference. But it's
29   // here anyways.
30   Serial.print("calibrating sensor ");
31   for (int i = 0; i < calibrationTime; i++) {
32     Serial.print(".");
33     delay(1000);
34   }
35   Serial.println(" done");
36   Serial.println("SENSOR ACTIVE");
37   delay(50);
38 }
39 
40 void loop()
41 {
42   if (analogRead(0) < threshold) {
43     if (lockLow) {
44       // Turn on the LED by setting the pin to OUTPUT and LOW
45       pinMode(ledPin, OUTPUT);
46       digitalWrite(ledPin, LOW);
47 
48       //makes sure we wait for a transition to LOW before any further output is made:
49       lockLow = false;
50       Serial.println("---");
51       Serial.print("motion detected at ");
52       Serial.print(millis()/1000);
53       Serial.println(" sec");
54       delay(50);
55     }
56     takeLowTime = true;
57   }
58 
59   if (analogRead(0) > threshold) {
60 
61     if (takeLowTime) {
62       lowIn = millis();          //save the time of the transition from high to LOW
63       takeLowTime = false;       //make sure this is only done at the start of a LOW phase
64     }
65     //if the sensor is low for more than the given pause,
66     //we assume that no more motion is going to happen
67     if (!lockLow && millis() - lowIn > pause) {
68       // Turn off the LED by setting the pint to INPUT
69       pinMode(12, INPUT);
70 
71       //makes sure this block of code is only executed again after
72       //a new motion sequence has been detected
73       lockLow = true;
74       Serial.print("motion ended at ");      //output
75       Serial.print((millis() - pause)/1000);
76       Serial.println(" sec");
77       delay(50);
78     }
79   }
80 }

My version "Smart Motion A-06" "Ver:RB-S04" "2008/08/05"

Nice usage... https://picasaweb.google.com/patyoungers/MunnyDollNightlightInfraredDetector?authkey=Gv1sRgCK7Ao_e2lZrfsgE#

Original PIRsense: http://www.arduino.cc/playground/Code/PIRsense

Weight

As of today (2nd April 2012) the component weights are as follows

  • 1665g = baseplate, hubs, wheels, motors, alu eeePC bracket
  • 1246g = eeePC w. battery and holding box
  • 294g = motor batteries 12x AA, 3x cases
  • 2216g = cheesoid body: cardboard cylinder, eyes, top and side panels, LCD display, mode switch, keyswitch and power buttons

Total weight 5421g!!!!!!!

Details

These details would be better on a subpage rather than a talkpage. Jon (talk) 21:20, 24 April 2019 (UTC)