Kaluma-9 (K9) update

 UPDATE:

okay, as previously mentioned I spent a little bit of time (on a Saturday evening) looking to use a Raspberry Pi PICO W and a Robo Pico board, along with a very old Dr.Who K9 robot toy to create a functioning "modern day" object avoidance detection robot.  No rocket science going on here, execpt me being me, I wanted to use JavaScript and NOT Python to control everything.

Why JavaScript? Well, when coding for Arduino you are using C.  The cleanest and most sensible coding language to use (ignore all that modern day OOP rubbish that people attempted to shove on/around it - that just ruined it, sometimes Software Architect are NOT right & if you accept that, you then chose what is best for the job, rather than forcing everything to fit what you know, bit of a life lesson there, me's thinks) for the task at hand whilst running on constrained embedded devices.  Well, I'm not using an Arduino atm, however, I might, so to make the "switch" easier, I decided to use Kaluma on the Pico W, which is in essence a JavaScript Engine - VERY similar to the one I was working with at AvantGo back in 2000-ish running on Palm PDAs and Compaq PDA devices along with an offline web-browser (the ex-netscape devs had hacked it from the remains of the netscape browser wars - I'll write a book one day), so simpler and easier for me to use.

As mentioned previously, I got so far on the Saturday and was going to return on the Sunday.  Which, I actually did!  okay, it was in mid-afternoon, but that was good enough.

I had the basics working, I had the ability to prod & poke the digital and analog pins, I could get readings from the HC-SR04 ultrasonic sensors, now, I just needed to get the motors to work.  I noticed there wasn't actually a library / package available (I now might write one?), so I scanned the previous CircuitPython code and noticed it was actually compiled so I couldn't read it - I'm assuming that is for size/compression reasons? anyway, I noticed reference to frequency and duty cycle settings that indicated to me that the Python code was interacting with the motors as PWM (Pulse Width Modulation) and Kaluma has that as a native library - no package needed.  I then set about doing some debug testing and lo-and-behold, within 5mins I was able to trigger the left motor and spin the wheel and then the right motor and the wheel.... and then I was able to control the speed, so I could go 'flat out' or I could go 'half speed'.  Why is that important? well, that is actually how the K9 turns left or right!  you go forward with the right wheel and backwards with the left wheel to turn left, but if you do it at full speed you spin on the spot!  so you must be able to vary the speed to make it smoother and have more control.

This is left here more for my reference than anything else.

Appreciate that the code is not the cleanest or most efficient, but for PoC it works & does the investigation job.  I even highlighted to myself that as I put this code within a loop I can never call .close() on the motors, not sure if that will ever be a problem?  I'll come to that issue when I add a HTTP web-server into the equation, then I'll be able to handle doing such things as it'll be a re-write to cater for that sense of state-machine.

User Requirement: (LOL)

Right, my objective was to turn on the wheeled motors (still using the original plastic gearing so they are mega-loud and retro sounding) so the K9 is always moving and then use the HC-SR04 ultrasonic sensor to detect any objects within it's PING zone (appreciate this is currently a limitation, but y'know start small), if objects are >5cm and <10cm away, then move left or right until the detection distance is further - this indicates that there is a way past the object, decide which gave the most distance and then move forward in that direction, if the object is now <5cm away then either something materialised from another dimension infront of it (or I placed an object there to freak it out) or we didn't get an accurate reading before but now we do, so back up, basically go in reverse for a couple of seconds until the distance is >10cm.  As I say, this is all basic object avoidance and will give the perception that the K9 is navigating around its environment "making decisions" about where to go etc... until it either runs out of battery or drives off the top of the stairs to its doom.  That's sufficient requirements to get going & as with everything code-wise, as I shall show, you don't have to get it ALL working first go, I'll do it in increments, prove things out, learn from the behaviours and then build on that in the next step.


Now let's turn that into some code.  As you can see I've lent into the Arduino style template of declaration, setup (run once) and then loop, that runs forever.


//import library to detect what Pico is being used - Pico W

const { PicoCYW43 } = require('pico_cyw43');

const pico_cyw43    = new PicoCYW43();

//import ultrasonic sensors library

const {HCSR04}      = require('hc-sr04');

const echo_pin      = 17;

const trigger_pin   = 16;

const hcsr04        = new HCSR04(trigger_pin, echo_pin);

//import PWM library for motors

const { PWM } = require('pwm');

const m1a_pin       = 8;

const m1b_pin       = 9;

const m2a_pin       = 10;

const m2b_pin       = 11;

const frequency     = 50000;

const duty          = 0;

const stop          = 0;

const half_speed    = 0.5;

const full_speed    = 1;

const creep_fwd     = 0.1;

const creep_bwd     = -0.1;

const pwm_m1a       = new PWM(m1a_pin, frequency, duty);

const pwm_m2a       = new PWM(m2a_pin, frequency, duty);

//BIG blue LED

const bigblue_pin   = 4;

console.log('init complete');

//just like arduino C coding


//setup goes code here

//set motor pins as output

pinMode(m1a_pin, OUTPUT);

pinMode(m1b_pin, OUTPUT);

pinMode(m2a_pin, OUTPUT);

pinMode(m2b_pin, OUTPUT);

//set bigblue pin as output

pinMode(bigblue_pin, OUTPUT);

//pinMode(x, INPUT);


// loop code goes in here

//this can be defined as EITHER of these, they are the same:

//setInterval(() => {

//setInterval(function() {


setInterval(() => {

  //let's just have a heartbeat flash every second

  //could also do the BIG blue one as it's on the K9 and removes the need for this library code

  if (pico_cyw43.getGpio(0)) {

    pico_cyw43.putGpio(0, false); // turn-off LED

    digitalWrite(bigblue_pin, LOW);

  } else {

    pico_cyw43.putGpio(0, true); // turn-on LED

    digitalWrite(bigblue_pin, HIGH);

  }


  //get the distance detected from the sensors

  let dist = hcsr04.distance();

  console.log('get distance:'+dist);

  if(dist != null) {

    console.log(`Distance is ${dist} mm`);

  } else {

    console.log('Failed to measure distance');

  }

  

  //move motors depending upon the distance away logic

  //if object > 10cm (100mm) away = move forwards - go towards object

  //if object < 10cm (100mm) > 5cm (50mm) away = stop and think

  //if object < 5cm (50mm) away = move backwards - run away from object

  if(dist < 50) {

    console.log('obj less than 5cm away');

    //move();

  } else if((dist >50) && (dist <100)) {

    console.log('obj less than 10cm away but further than 5cm');

    //move();

  } else if(dist >100) {

    console.log('object further than 10cm away');

    //move();

  }

  

  //as this will run FOREVER, we shall never call the pwm_xxx.close(); to close the ports of the PWM motors  

}, 1000); //loops every 1 second


function move(obj, duty) {

  obj.setDuty(duty);

  console.log('moving...'+duty);

  obj.start();

  delay(500); // run at the duty speed for 0.5seconds

  obj.stop();

}

What happens when you run it?  Here's some debug output:

> .load
init complete
get distance:null
Failed to measure distance
obj less than 5cm away
get distance:181.05
Distance is 181.05 mm
object further than 10cm away
get distance:94.52
Distance is 94.52 mm
obj less than 10cm away but further than 5cm
get distance:16.49
Distance is 16.49 mm
obj less than 5cm away
get distance:135.32
Distance is 135.32 mm
object further than 10cm away
get distance:168.30
Distance is 168.30 mm
object further than 10cm away


As you can see, the init happened, for some reason the first scan failed, but then it worked, it pinged to the full distance 18cmm away, which is about right for where the sensors are placed and where a monitor shelf is in front of them.  Then I put my hand in the way and we got a 9.4cm distance, which was detected to be >5cm and <10cm and then I moved my hand a lot closer to 1.6cm and we got the <5cm detection, then I moved my hand out of the way completely and if I'd let it go a few more times it would have resorted back to the 181.05mm value.

So there we have it, the code is doing what it should, you can see the commented out move() code to move the motors for the driving piece.

I'm umming and erring at this point, do I get the WiFi connectivity coded into place so I can use a web-browser wither on the phone or laptop to then drive/control the K9, I'll be needing this for when the XIAO ESP32S3 camera is plugged in for visual detection & recognition, or I just do the simple coding for now and rebuild the codebase next week?

Whilst I ponder here's a short video to prove that the PoC motor code does work!

Here's the K9:



and here's the code executing and debug output (that led into the code above)



....

on that note, I'll go read all about "Dimensions" by Mr.Vallee & then get some sleep & get back to the day job tomorrow.

Comments