Simple single axis light source direction tracker

Posted on
  • Something I had in mind for a while, but never got to it: A light tracker (sun tracker).

    Two LDRs - light dependent resistors - separated by an 'vertical, flat wall' mounted on a platform catch the light. When the light is in the plane of the wall - right above - both LDRs get about the same amount of light and pose about the same resistance. Each LDRs is series with a resistor between the power rails 0 - and 3.3V - and builds a voltage divider. The voltage at the divider points is sampled several times and then compared. If different by more as a given delta, a state information switches the values of -1, 0, +1. This state is used to trigger a tracker that rotates the platform - longitudinal to the wall - with a stepper until both LDRs catch about the same light again. To get the best results, I paired the LDRs (and series resistors) from a bunch to get two of each with most 'equal' specs.

    The code started out with just working the LDRs and showing green or red LED of PICO with flashing of both on polled light sensing. I added - recycled - some old code of mine - Stepper Motor Spinning Slow from almost 7 years ago - to rotate the platform. I linked both the light sensing and stepper motor code together with a tracker function. Initially, I called the tracker on an interval as well, but that kept the platform oscillating. In a second version tracking, I called the tracking on change of light sensing state. But it was still overshooting and oscillating, except I made the rotation really, really slow, so that about a step or two per light sampling. Lastly - what is shown - is a combination of both, trigger rotation or stopping of it on intervalled light sampling AND check with stopping of rotation on every step by taking 'quickly' a simple light sample.

    The attached clip shows the sensor platform 'following' the light desk lamp which I move back and forth. Notice a small correctional adjustment after a major adjustment - in seconds 11 and 16. - seen by a short 'after' flashing of the stepper LEDs. This is because the LDR is slow reacting (and also because only one sample read is taken for triggering the stop of rotation on every stepper step (Every 4[ms] a step is made. The rotation is so slow because of the high reduction gear as part of the motor.)

    The code - see next post - is far from prime time... but it shows that with a few lines something can be done quickly. For real worl used, limits have to be introduced - with 'hard range end detection / switches' - and detection of false adjustment triggers. In the end, this tracker will serve a single or with a double a two axis solar tracker. Given real time, values in tables for each week of an earth-sun year for the geo-location and a few formulas, the trackers position is primed and uses presented contraption for fine adjustment.


    2 Attachments

  • Here is the code:

    // ldrCtrl.js
    // 2001-12-12 - 1st cut: cludged code... :\
    // this: digital control loop... tracking speed constant, delta != 0
    // next: -- reverse logic driver from sensing to tracking
    //       -- analog control loop... tracking speed proportianl to delta
    
    // --------- general ---------
    
    var c = 0; // console
    
    // --------- light sensing code / LDR ---------
    
    var lsPs =
      [ A3  // 0
      , A4  // 1
      ]
      , lsDr = [ "ns", "-N", "-S" ] // direction
      , cw = 6
      , iId
      , sampleCnt = 5  // times
      , sampleTme = 100 // [ms]
      , sampleBrk = 300 // [ms]
      , tstIntTme = sampleCnt*sampleTme + sampleBrk // [ms]
      , ctlIntTme = tstIntTme * 2 // [ms]
      , ctlSteps  = 5
      , delta = 10 // [mV] on 330[mV] w/ office lamp 18" away
      , lsState = 0
      ;
    
    function test(vals,cnt,res) {
      var idx = lsPs.length;
      if (vals === undefined) {
        vals = [];
        while (--idx>=0) vals.push(0);
        idx = lsPs.length; cnt = sampleCnt;
      }
      while (--idx>=0) vals[idx]+=analogRead(lsPs[idx]);
      if (--cnt>0) {
        setTimeout(()=>test(vals,cnt),sampleTme)­;
      } else {
        idx = vals.length;
        while (--idx>=0) {
           vals[idx] = Math.round(vals[idx]/sampleCnt*3.3*1000)­;
        }
        // vals.push(Math.round((vals[0]+vals[1])/2­));
        vals.splice(0,0,Math.round((vals[0]+vals­[1])/2));
        idx = vals.length; 
        if (c) {
          res = [];
          while (--idx>=0) res.push(fmt(vals[idx],cw)+lsDr[idx]);
          console.log(res.join(""));
        }
        if (Math.abs(vals[0]-vals[1]) < delta) {
          lsState = 0; LED1.set(); LED2.set();
          setTimeout(()=>{ LED1.reset(); LED2.reset(); },20);
        } else {
          if (vals[0] < vals[1]) {
            lsState = -1; LED1.set(); LED2.set();
            setTimeout(()=>{ LED2.reset(); },20);
          } else {
            lsState = +1; LED1.set(); LED2.set();
            setTimeout(()=>{ LED1.reset(); },20);
          }
        }
        if (trOn) track();
      }
    }
    
    function lsR() { lsH(); iId = setInterval(test,tstIntTme); }
    function lsH() { if (iId) iId = clearInterval(iId); }
    
    function lsIni() {
      // setBusyIndicator(LED1);
      idx = lsPs.length;
      while (--idx>=0) pinMode(lsPs[idx],"analog");
    }
    
    // --------- stepper code ---------
    
    // stRn  stepper status: is running
    // stPs  stepper pins
    // stBW  steps Backwards bit ON/OFF pattern
    // stFW  steps Forward   bit ON/OFF pattern
    // stSt  step bit Stop / OFF pattern
    // stT   step Time in milliseconds [ms]
    // stI   step Interval (from setInterval() and for clearInterval()
    // sts   steps 0001,0011,0010,... pin ON/OFF pattern in use
    // st    step 0..7 current step
    // stDmy ...because of lint / (cond) ? exprT : exprF needs something to assign to
    var stRn = false;
    var stPs = [B4,B5,B6,B7];
    var stFW = [0b1000,0b1100,0b0100,0b0110,0b0010,0b00­11,0b0001,0b1001];
    var stBW = [0b1001,0b0001,0b0011,0b0010,0b0110,0b01­00,0b1100,0b1000];
    var stSt =  0b0000;
    var stCk = ()=>0;
    var st  = 0;
    var stT = 0;
    var stI = null;
    var sts = null;
    var st  = 0;
    var stDmy;
    
    // setI setInterval(i,stsC) i in [ms] with (optionl) step Change (if not null), 
    // and direction info (string)
    var setI = function(t,stsN,d) {
      if (c) console.log("t = ",t, d); 
      if (stI) clearInterval(stI);
      if (stsN) sts = stsN;
      stRn = true;
      stI = setInterval(stp,t);
    };
    // stp step
    var stp = function() { digitalWrite(stPs, sts[st = ++st % 8]); stCk(); };
    // _sFW step ForWard
    var _sFW = function(t) {
      if (c) console.log("FW w/ " + t);
      if (stT > 0) { setI((stT = t),null," ~F");
      } else { if (stT) { st = Math.abs(st - 7); } if (!stI) { st--; } setI((stT = t),stFW," FW"); }
    };
    // _sBW step BackWards
    var _sBW = function(t) {
      if (c) console.log("BW w/ " + t);
      if (stT < 0) { setI(-(stT = t),null," ~B"); 
      } else { if (stT) { st = Math.abs(st - 7); } if (!stI) { st--; } setI(-(stT = t),stBW," BW"); }
    };
    
    // stH stepper halt function
    var stH = function() {
      if (c) console.log("stepper halt");
      if (stI) { stI = clearInterval(stI); stI = null; }
      if (c) console.log(stI);
      stRn = false;
      digitalWrite(stPs, stSt);
    };
    
    // stR stepper run function - t is stepping interval in [ms]
    var stR = function(t,ck) {  
      if (typeof t === "undefined" ) {
        if (stT) {
          if (c) console.log((stT > 0) ? "F>B" : "B>F");
          stR(-stT,ck);
        } else {
          if (c) console.log("What ?");
        }
      } else {
        stCk = (ck) ? ck : ()=>0;
        stDmy = (t) ? (t>0) ? _sFW(t) : _sBW(t) : stH(); 
      }
    };
    
    // --------- track code ---------
    
    var trLsState = 0 // previous lsState
      , trOn = false  // tracking on
      , trStTme =  4  // [ms] track step time
      ;
    
    // track function
    function track() { if (lsState != trLsState) {
      stH();
      if (lsState != 0) stR((lsState<0) ? +trStTme : - trStTme
        , ()=> {
            if (Math.abs(Math.round((analogRead(lsPs[0]­)-analogRead(lsPs[1]))
              *3.3*1000))<delta) stH(); trLsState = 0;
               });
      trLsState = lsState;
    } }
    
    // track run function
    function trR() { trOn = true; }
    
    // track halt function
    function trH() { stH(); trOn = false; }
    
    // --------- general ----------
    
    // format (integer) value v(+|-) to ds(<=7|8) digits
    function fmt(v,ds) {
      var s = "      "+v, l = s.length;
      return s.substr(l-ds);
    }
    
    function onInit() {
      lsIni(); // light sensing LDR analog pin init
      lsR();   // light sensing run
      trR();   // tracking run
    }
    
    setTimeout(onInit,999); // remove before upload for save()
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Simple single axis light source direction tracker

Posted by Avatar for allObjects @allObjects

Actions