{"id":52287,"date":"2026-11-01T10:50:51","date_gmt":"2026-11-01T10:50:51","guid":{"rendered":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/"},"modified":"2026-11-01T10:50:51","modified_gmt":"2026-11-01T10:50:51","slug":"line-following-robot","status":"publish","type":"siratek_tutorial","link":"https:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/","title":{"rendered":"Project, a Line Following Robot"},"content":{"rendered":"<body><p><\/p>\n<h2>What you will build<\/h2>\n<p>A robot that follows a line of black tape across the floor. Four sensors looking down, two geared motors, and a sketch that turns \u201cthe line is slightly to my left\u201d into \u201cslow the left wheel\u201d.<\/p>\n<p>It is the first project in this library that is a <strong>closed loop<\/strong>: the thing it measures is changed by the thing it does, continuously, and that changes how you think about all of it. Everything in the two component pages that was a problem \u2014 two motors that will not run at the same speed, a sensor with no analog output, a trim that is only right at one speed \u2014 stops mattering here, because the robot corrects its own heading several hundred times a second.<\/p>\n<p>That is the lesson, and it is worth more than the robot.<\/p>\n<h2>Before you begin<\/h2>\n<h3>Parts<\/h3>\n<table>\n<thead>\n<tr>\n<th>Part<\/th>\n<th>Siratek product<\/th>\n<th>Its own tutorial<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Arduino Uno<\/td>\n<td>2219<\/td>\n<td><em>Getting Started with Arduino Uno<\/em><\/td>\n<\/tr>\n<tr>\n<td>Four-way line tracking sensor<\/td>\n<td>2943<\/td>\n<td><em>A Four-Way Line Tracking Sensor<\/em><\/td>\n<\/tr>\n<tr>\n<td>L298N motor driver<\/td>\n<td>2533<\/td>\n<td><em>Driving DC Motors with an L298N<\/em><\/td>\n<\/tr>\n<tr>\n<td>Geared motor and wheel, <strong>two of them<\/strong><\/td>\n<td>7406<\/td>\n<td><em>Geared Motors and Wheels for a Robot<\/em><\/td>\n<\/tr>\n<tr>\n<td>Push button<\/td>\n<td>2353<\/td>\n<td><em>Read a Push Button<\/em><\/td>\n<\/tr>\n<tr>\n<td>Breadboard and jumper wires<\/td>\n<td><\/td>\n<td><em>Using a Breadboard<\/em><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Also needed: a <strong>chassis<\/strong> or something to screw the motors to, a castor or ball wheel for the front, a <strong>battery pack of four to six AA cells<\/strong> for the motors, a soldering iron, black electrical tape and a large sheet of white paper or card.<\/p>\n<p><strong>Siratek product 7406 is one motor and one wheel.<\/strong> A driving robot needs two of them. That is the single most common ordering mistake on this project.<\/p>\n<h3>The motor supply, which is not optional<\/h3>\n<p>Each geared motor draws roughly 150 to 200 mA running free and <strong>close to 1 A stalled<\/strong>. Two stalled motors is near 2 A, which is far beyond USB and more than a tired battery pack will hold up under.<\/p>\n<p><strong>A robot that resets when it bumps into something is this, not a software fault.<\/strong> Separate motor supply, grounds joined, as the L298N page sets out. There is no sketch that fixes a sagging supply.<\/p>\n<h3>Why the motor mismatch stops mattering<\/h3>\n<p><em>Geared Motors and Wheels for a Robot<\/em> is blunt about it: two identical motors given the same voltage make a robot curve, every time, often by ten percent or more, and the three honest responses are to trim it, to fit encoders, or to <strong>build something that corrects its own heading<\/strong>.<\/p>\n<p>This project is the third one. The line tells the robot where it is several hundred times a second, and a wheel that runs a little fast simply gets a little less of the correction. You never have to measure the trim at all.<\/p>\n<p>Which is why a line follower is the right first robot, rather than the hard one it looks like.<\/p>\n<h3>Six pins for the driver, four for the sensor, and the collision between them<\/h3>\n<p>The L298N takes <strong>six<\/strong> Arduino pins, and the page assigns them <code>D5<\/code> to <code>D10<\/code> because three of those are PWM-capable, which the enable pins need.<\/p>\n<p>The tracking sensor\u2019s own page uses <code>D4<\/code> to <code>D7<\/code>. Those overlap. Something has to move, and it must not be the enable pins, because PWM is not available everywhere.<\/p>\n<p>So the sensor goes on <strong><code>A0<\/code> to <code>A3<\/code>, used as digital pins.<\/strong> The analog inputs on an Uno are ordinary digital pins as well \u2014 <code>A0<\/code> is <code>D14<\/code>, <code>A1<\/code> is <code>D15<\/code>, and so on \u2014 and <code>digitalRead(A0)<\/code> works exactly as <code>digitalRead(4)<\/code> does. Nothing is lost here, because the sensor has no analog output for us to give up.<\/p>\n<p>This is a small thing that catches people out on every project with more than two modules in it: <strong>pins are a budget<\/strong>, and the ones with special abilities get spent last.<\/p>\n<h3>Set the trimmers before you write any logic<\/h3>\n<p>The tracking sensor has four trimmers and four indicator LEDs, and nothing works properly until they are set. Lay out your line, hold the sensors at their mounted height over the white card, and turn each trimmer until its LED just changes, then back it off slightly.<\/p>\n<p><strong>Height matters more than anything else.<\/strong> Five to fifteen millimetres is the usual working range, all four heads must sit at the same height, and a head two millimetres low behaves like a differently adjusted one. Fix the height mechanically; do not try to fix it in software.<\/p>\n<p>The full procedure is on the sensor\u2019s page. Do it with the robot on a stand, before the motors are ever enabled.<\/p>\n<h3>Build the line before you build the robot<\/h3>\n<p>Black electrical tape on white card, at least two metres of it, with gentle curves. <strong>Make the first one easy<\/strong>: wide radius, no crossings, no sharp corners.<\/p>\n<p>A tight corner is not a harder version of the same problem \u2014 it is a different problem, because the line leaves the sensor array completely and the robot has to search for it. Get the easy course working first, then make it harder deliberately.<\/p>\n<h2>How it works<\/h2>\n<p>One loop, running as fast as it can, doing three things:<\/p>\n<table>\n<thead>\n<tr>\n<th>Step<\/th>\n<th>What happens<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Read<\/td>\n<td>Four <code>digitalRead()<\/code> calls become one position number<\/td>\n<\/tr>\n<tr>\n<td>Decide<\/td>\n<td>The position becomes a left speed and a right speed<\/td>\n<\/tr>\n<tr>\n<td>Act<\/td>\n<td>Two <code>analogWrite()<\/code> calls<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>There is no <code>delay()<\/code> anywhere, and that is not a style preference. A delay of fifty milliseconds at walking pace is several centimetres of travel with its eyes shut.<\/p>\n<h3>The position number<\/h3>\n<p>From the sensor\u2019s own page: each head reads light or dark, the dark ones are weighted <code>-3, -1, +1, +3<\/code> from left to right, and averaging the weights of the heads that see the line gives a single number saying how far off-centre it is. Negative is left, positive is right, zero is centred, and <code>NO_LINE<\/code> is a separate answer that is <strong>not<\/strong> zero.<\/p>\n<p>That last distinction is the one that keeps the robot on the table. A sketch that treats \u201cno line\u201d as \u201ccentred\u201d drives confidently straight on.<\/p>\n<h3>Proportional steering<\/h3>\n<p>The whole controller is one idea: <strong>steer in proportion to how far off you are.<\/strong><\/p>\n<pre><code>left  = base + gain * position\nright = base - gain * position<\/code><\/pre>\n<p>A little off-centre, a little correction. Far off, a big one. The robot slows the wheel on the side the line is on, which turns it that way.<\/p>\n<p><code>gain<\/code> is the number you tune, and it has two failure modes you will meet in this order:<\/p>\n<table>\n<thead>\n<tr>\n<th>Gain<\/th>\n<th>Behaviour<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Too low<\/td>\n<td>Drifts off the line on curves; corrects too lazily<\/td>\n<\/tr>\n<tr>\n<td>About right<\/td>\n<td>Follows the line with a gentle weave<\/td>\n<\/tr>\n<tr>\n<td>Too high<\/td>\n<td>Oscillates violently, left-right-left, and often loses the line entirely<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Start low, raise it until the weave appears, then back off slightly. That is the whole tuning procedure, and doing it by hand once teaches more about control than reading about it does.<\/p>\n<p><strong>This is proportional control only<\/strong>, the P of PID. Adding the other two terms is a real improvement at speed and a project of its own; at the speed a first robot should run, P alone follows a reasonable line well.<\/p>\n<h3>Speed is the hidden variable<\/h3>\n<p>Everything above depends on how fast the robot is going. A gain tuned at a crawl oscillates at speed, because the same correction has longer to act.<\/p>\n<p>So <strong>tune slowly first.<\/strong> <code>BASE_SPEED<\/code> of about 120 is a sensible start. Raising the speed later means re-tuning the gain, and that relationship \u2014 faster needs gentler, or needs a better controller \u2014 is worth noticing because it is true of every control loop you will ever write.<\/p>\n<h3>Losing the line, which is normal<\/h3>\n<p>Especially on corners. The behaviour you choose defines how the robot feels, and the simplest useful rule is: <strong>remember which way you were last turning, and keep turning that way.<\/strong> If the line vanished to the left, it is probably still to the left.<\/p>\n<p>With a time limit on it. Searching for a second is persistence; searching forever is a robot spinning in the corner of the room until its batteries go flat.<\/p>\n<h3>All four dark<\/h3>\n<p>Four sensors reading dark at once is not a line. It is a junction, the end of the course marked with a wide band, or the sensors lifted off the surface. The sketch reports it separately and stops, which is the honest default \u2014 deciding what a junction means is a choice about your course, not about the robot.<\/p>\n<h2>Build it in stages<\/h2>\n<p>Each stage ends with something working. Do not skip to the end; a robot that drives before its sensors are trusted is a robot that drives off the bench.<\/p>\n<h3>Stage 1: the mechanics<\/h3>\n<p>Solder leads to both motors <strong>before<\/strong> mounting them \u2014 afterwards the tabs are awkward to reach and they break off easily. A dab of hot glue over each joint earns its place.<\/p>\n<p>Screw both motors to the chassis, wheels pushed fully onto the D-shaped shafts, castor at the front. Mount the four sensor heads in a row, facing down, <strong>all at the same height<\/strong>, spaced so the line falls between the middle two when the robot is centred.<\/p>\n<p>Because the heads are separate boards on leads, you can space them to suit the chassis rather than building the chassis around the sensor. Use that.<\/p>\n<h3>Stage 2: the motors, on a stand, one at a time<\/h3>\n<p>Wire the L298N as its page describes \u2014 enable jumpers <strong>off<\/strong>, six logic wires, separate supply, grounds joined \u2014 and run the geared motor page\u2019s one-at-a-time test with the wheels off the ground.<\/p>\n<p><strong>One wheel will turn backwards.<\/strong> The two motors face opposite ways on the chassis, so this is expected rather than puzzling. Swap that motor\u2019s two wires at the terminal block.<\/p>\n<p>Find the starting duty cycle too: step up from a standstill until both wheels turn reliably. Often around 60 to 80. Below that they hum and do not move.<\/p>\n<h3>Stage 3: the sensors, still on the stand<\/h3>\n<p>Set the four trimmers. Then run the sensor page\u2019s display sketch and slide the robot across the line by hand, watching the pattern move: <code>. # . .<\/code> becoming <code>. . # .<\/code>.<\/p>\n<p><strong>Confirm which physical head is which<\/strong>, by covering them one at a time. The ribbon can be plugged in either way round, and a reversed array makes a robot that steers away from the line \u2014 which looks like a broken gain and is not.<\/p>\n<p>Confirm the polarity as well. On most of these boards the output is LOW over black; check yours.<\/p>\n<h3>Stage 4: position, without motors<\/h3>\n<p>Run the position sketch from the sensor page and slide the robot across the line again, watching the number go <code>-3, -1, +1, +3<\/code>. Check that lifting it off the card gives <code>NO_LINE<\/code> and not <code>0<\/code>.<\/p>\n<h3>Stage 5: closed loop, on the stand<\/h3>\n<p>The complete sketch, with the wheels still off the ground. Move the line under the robot by hand and watch the wheels respond: the line to the left should slow the left wheel.<\/p>\n<p>If they respond the wrong way, the array is reversed or the motors are swapped. Fix it here, not on the floor.<\/p>\n<h3>Stage 6: the floor<\/h3>\n<p>Put it down on the easy course, press the button, and tune the gain.<\/p>\n<h2>Pin reference<\/h2>\n<table>\n<thead>\n<tr>\n<th>Device<\/th>\n<th>Pin<\/th>\n<th>Connect to<\/th>\n<th>Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>L298N<\/td>\n<td><code>ENA<\/code><\/td>\n<td>Uno <code>D10<\/code><\/td>\n<td>Left speed, PWM, jumper <strong>off<\/strong><\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>IN1<\/code><\/td>\n<td>Uno <code>D9<\/code><\/td>\n<td>Left direction<\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>IN2<\/code><\/td>\n<td>Uno <code>D8<\/code><\/td>\n<td>Left direction<\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>IN3<\/code><\/td>\n<td>Uno <code>D7<\/code><\/td>\n<td>Right direction<\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>IN4<\/code><\/td>\n<td>Uno <code>D6<\/code><\/td>\n<td>Right direction<\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>ENB<\/code><\/td>\n<td>Uno <code>D5<\/code><\/td>\n<td>Right speed, PWM, jumper <strong>off<\/strong><\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>OUT1<\/code>, <code>OUT2<\/code><\/td>\n<td>Left motor<\/td>\n<td>Either way round; corrected in stage 2<\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>OUT3<\/code>, <code>OUT4<\/code><\/td>\n<td>Right motor<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>L298N<\/td>\n<td><code>+12V<\/code>, <code>GND<\/code><\/td>\n<td>Battery pack, <strong>and Uno <code>GND<\/code><\/strong><\/td>\n<td>Grounds joined<\/td>\n<\/tr>\n<tr>\n<td>Sensor<\/td>\n<td><code>VCC<\/code><\/td>\n<td>Uno <code>5V<\/code><\/td>\n<td>Board and emitters<\/td>\n<\/tr>\n<tr>\n<td>Sensor<\/td>\n<td><code>GND<\/code><\/td>\n<td>Uno <code>GND<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Sensor<\/td>\n<td><code>D1<\/code><\/td>\n<td>Uno <code>A0<\/code><\/td>\n<td>Leftmost, used as a digital pin<\/td>\n<\/tr>\n<tr>\n<td>Sensor<\/td>\n<td><code>D2<\/code><\/td>\n<td>Uno <code>A1<\/code><\/td>\n<td>Centre left<\/td>\n<\/tr>\n<tr>\n<td>Sensor<\/td>\n<td><code>D3<\/code><\/td>\n<td>Uno <code>A2<\/code><\/td>\n<td>Centre right<\/td>\n<\/tr>\n<tr>\n<td>Sensor<\/td>\n<td><code>D4<\/code><\/td>\n<td>Uno <code>A3<\/code><\/td>\n<td>Rightmost<\/td>\n<\/tr>\n<tr>\n<td>Button<\/td>\n<td>one leg<\/td>\n<td>Uno <code>D2<\/code><\/td>\n<td><code>INPUT_PULLUP<\/code>, start and stop<\/td>\n<\/tr>\n<tr>\n<td>Button<\/td>\n<td>other leg<\/td>\n<td>Uno <code>GND<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Status LED<\/td>\n<td><\/td>\n<td>Uno <code>D13<\/code><\/td>\n<td>The onboard one; on means lost<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<figure class=\"siratek-tutorial-figure\"><img decoding=\"async\" src=\"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/line-following-robot-wiring.svg\" alt=\"A line following robot with four tracking heads on the analog pins used as digital inputs, an L298N on pins 5 to 10 driving two geared motors from a battery pack, and a start button, showing the proportional correction from the line position\" loading=\"lazy\"><figcaption>The driver needs six pins and three of them must be PWM, so the sensor moves to A0 to A3 \u2014 ordinary digital pins by another name.<\/figcaption><\/figure>\n<h2>The complete sketch<\/h2>\n<pre><code>\/\/ Siratek: a line following robot.\n\/\/ Proportional steering from a four-head tracking sensor.\n\n\/\/ Driver pins. ENA and ENB must be PWM-capable.\nconst int enA = 10, in1 = 9, in2 = 8;     \/\/ left\nconst int enB = 5,  in3 = 7, in4 = 6;     \/\/ right\n\n\/\/ The sensor, on the analog pins used as digital inputs.\nconst int sensorPins[4] = {A0, A1, A2, A3};\nconst int weights[4] = {-3, -1, 1, 3};    \/\/ leftmost to rightmost\n\nconst int buttonPin = 2;\nconst int statusLed = 13;\n\n\/\/ ---- Tuned on YOUR course. Start here and adjust. ----\nint BASE_SPEED = 120;        \/\/ slow, until the gain is right\nfloat GAIN = 18.0;           \/\/ raise until it weaves, then back off\nconst int MINIMUM_SPEED = 70;   \/\/ below this the motors hum\nconst int SEARCH_SPEED = 100;\n\/\/ ------------------------------------------------------\n\nconst unsigned long LOST_GIVE_UP = 1200;   \/\/ stop searching after this\n\nconst int NO_LINE  = 99;\nconst int ALL_DARK = 98;\n\nbool running = false;\nint lastSeen = 0;\nunsigned long lostSince = 0;\nunsigned long lastButton = 0;\n\nvoid leftMotor(int speed) {\n  digitalWrite(in1, speed &gt;= 0);\n  digitalWrite(in2, speed &lt; 0);\n  analogWrite(enA, abs(speed));\n}\n\nvoid rightMotor(int speed) {\n  digitalWrite(in3, speed &gt;= 0);\n  digitalWrite(in4, speed &lt; 0);\n  analogWrite(enB, abs(speed));\n}\n\nvoid drive(int left, int right) {\n  leftMotor(constrain(left, -255, 255));\n  rightMotor(constrain(right, -255, 255));\n}\n\nvoid halt() {\n  drive(0, 0);\n}\n\nint linePosition() {\n  int total = 0;\n  int seen = 0;\n\n  for (int i = 0; i &lt; 4; i++) {\n    \/\/ LOW over black on most of these boards. Confirm yours in stage 3.\n    if (digitalRead(sensorPins[i]) == LOW) {\n      total += weights[i];\n      seen++;\n    }\n  }\n\n  if (seen == 0) {\n    return NO_LINE;\n  }\n  if (seen == 4) {\n    return ALL_DARK;     \/\/ a junction, the end marker, or lifted off\n  }\n  return total \/ seen;\n}\n\nvoid setup() {\n  int pins[] = {enA, in1, in2, enB, in3, in4};\n  for (int i = 0; i &lt; 6; i++) {\n    pinMode(pins[i], OUTPUT);\n  }\n  for (int i = 0; i &lt; 4; i++) {\n    pinMode(sensorPins[i], INPUT);\n  }\n  pinMode(buttonPin, INPUT_PULLUP);\n  pinMode(statusLed, OUTPUT);\n\n  halt();\n  Serial.begin(9600);\n  Serial.println(F(\"Siratek: press the button to start. Press again to stop.\"));\n}\n\nvoid loop() {\n  \/\/ The button starts and stops, with a crude debounce.\n  if (digitalRead(buttonPin) == LOW &amp;&amp; millis() - lastButton &gt; 400) {\n    lastButton = millis();\n    running = !running;\n\n    if (!running) {\n      halt();\n      Serial.println(F(\"stopped\"));\n    } else {\n      lostSince = 0;\n      Serial.println(F(\"running\"));\n      \/\/ A brief full-power kick, because the motors will not start\n      \/\/ from rest at the speed we actually want.\n      drive(255, 255);\n      delay(60);\n    }\n  }\n\n  if (!running) {\n    halt();\n    digitalWrite(statusLed, LOW);\n    return;\n  }\n\n  int position = linePosition();\n\n  if (position == ALL_DARK) {\n    halt();\n    running = false;\n    digitalWrite(statusLed, HIGH);\n    Serial.println(F(\"all four dark: junction, end marker, or lifted off\"));\n    return;\n  }\n\n  if (position == NO_LINE) {\n    digitalWrite(statusLed, HIGH);\n\n    if (lostSince == 0) {\n      lostSince = millis();\n    }\n\n    if (millis() - lostSince &gt;= LOST_GIVE_UP) {\n      halt();\n      running = false;\n      Serial.println(F(\"line lost: stopped\"));\n      return;\n    }\n\n    \/\/ Keep turning the way we last saw it.\n    if (lastSeen &lt; 0) {\n      drive(-SEARCH_SPEED, SEARCH_SPEED);\n    } else {\n      drive(SEARCH_SPEED, -SEARCH_SPEED);\n    }\n    return;\n  }\n\n  \/\/ We can see the line.\n  digitalWrite(statusLed, LOW);\n  lostSince = 0;\n  lastSeen = position;\n\n  int correction = (int) (GAIN * position);\n\n  int left  = BASE_SPEED + correction;\n  int right = BASE_SPEED - correction;\n\n  \/\/ Do not ask a motor for a speed at which it only hums.\n  if (left &gt; 0 &amp;&amp; left &lt; MINIMUM_SPEED) {\n    left = MINIMUM_SPEED;\n  }\n  if (right &gt; 0 &amp;&amp; right &lt; MINIMUM_SPEED) {\n    right = MINIMUM_SPEED;\n  }\n\n  drive(left, right);\n}<\/code><\/pre>\n<h3>Four decisions worth explaining<\/h3>\n<p><strong>There is no <code>delay()<\/code> in the control path.<\/strong> The only one is the starting kick, which happens once when the button is pressed. At walking pace a fifty-millisecond delay is several centimetres driven blind, and that is what a robot sawing across the line usually turns out to be.<\/p>\n<p><strong><code>NO_LINE<\/code> and <code>ALL_DARK<\/code> are separate answers, and neither is zero.<\/strong> A position of <code>0<\/code> means centred. Collapsing either of the other two into it is the bug that drives a robot off a table, and it is an easy one to write by accident.<\/p>\n<p><strong><code>MINIMUM_SPEED<\/code> is applied after the correction, not before.<\/strong> The arithmetic happily produces a left speed of 30, at which the motor hums and does nothing while the right wheel drives on \u2014 so the robot turns much harder than intended. Raising small positive speeds to the threshold is the fix, and it is why the robot still steers properly at low base speeds.<\/p>\n<p><strong>Losing the line stops the robot rather than searching forever.<\/strong> Persistence for a second, then a stop. A robot that spins in a corner indefinitely is both useless and hard on the gearboxes.<\/p>\n<h2>Testing it<\/h2>\n<p>Stand first, floor second. Every line in this table is worth doing in order.<\/p>\n<table>\n<thead>\n<tr>\n<th>Try this<\/th>\n<th>It should<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>On a stand<\/strong>, slide the line to the left<\/td>\n<td>The left wheel slows<\/td>\n<\/tr>\n<tr>\n<td>On a stand, slide the line right<\/td>\n<td>The right wheel slows<\/td>\n<\/tr>\n<tr>\n<td>On a stand, lift it off the card<\/td>\n<td>Wheels search one way, then stop after a second<\/td>\n<\/tr>\n<tr>\n<td>Cover all four heads<\/td>\n<td>Stop, with the status LED on<\/td>\n<\/tr>\n<tr>\n<td>On the floor, gain too low<\/td>\n<td>Drift off on the curves<\/td>\n<\/tr>\n<tr>\n<td>Raise the gain until it weaves<\/td>\n<td>A gentle regular weave along a straight line<\/td>\n<\/tr>\n<tr>\n<td>Raise it further<\/td>\n<td>A violent oscillation; now you know the limit<\/td>\n<\/tr>\n<tr>\n<td>Back it off, run the course<\/td>\n<td>Follows it, including the curves<\/td>\n<\/tr>\n<tr>\n<td>Raise <code>BASE_SPEED<\/code> by 40<\/td>\n<td>Probably oscillates again: re-tune the gain<\/td>\n<\/tr>\n<tr>\n<td>Hold a wheel while it runs<\/td>\n<td>It should not reset. If it does, the supply is the problem<\/td>\n<\/tr>\n<tr>\n<td>Run it on a shiny floor<\/td>\n<td>Likely worse than on card; matte surfaces reflect usefully<\/td>\n<\/tr>\n<tr>\n<td>Run it in direct sunlight<\/td>\n<td>Likely erratic; these are indoor sensors<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The three gain rows are the project. Watching the same robot go from lazy to ideal to unstable because of one number is the clearest demonstration of feedback you can get on a bench, and it costs nothing but a few minutes on the floor.<\/p>\n<h2>Making it yours<\/h2>\n<ul>\n<li><strong>Add the full PID.<\/strong> The derivative term damps the weave and lets you run much faster; the integral term pulls out a persistent off-centre bias. Both are real improvements once P is tuned, and neither is worth adding before then.<\/li>\n<li><strong>Handle junctions.<\/strong> Right now all four dark stops the robot. Counting junctions, or always turning the same way at one, turns a line into a maze.<\/li>\n<li><strong>Fit encoders.<\/strong> The geared motors have a spare shaft on the other side, exactly for a slotted disc. Then the robot knows how far it has travelled as well as where the line is.<\/li>\n<li><strong>An obstacle sensor.<\/strong> An HC-SR04 on the front, and the robot stops instead of pushing a chair across the room. A servo under the sensor lets it look around before deciding.<\/li>\n<li><strong>A wider array.<\/strong> Five or more heads, spaced further apart, sees a sharp corner before it has lost the line.<\/li>\n<li><strong>Make it wireless.<\/strong> HC-05 Bluetooth to change the gain and base speed while it runs, which turns tuning from a dozen uploads into one session with a phone.<\/li>\n<\/ul>\n<p>That last one is the practical one. Tuning by editing a constant and re-uploading is slow enough that people stop before they have found the good value.<\/p>\n<h2>Troubleshooting<\/h2>\n<table>\n<thead>\n<tr>\n<th>Symptom<\/th>\n<th>Likely cause<\/th>\n<th>Check or fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Steers away from the line<\/td>\n<td>Sensor array reversed<\/td>\n<td>Cover each head and find the order<\/td>\n<\/tr>\n<tr>\n<td>Steers away, order correct<\/td>\n<td>Motor leads swapped<\/td>\n<td>One motor faces the other way; swap its wires<\/td>\n<\/tr>\n<tr>\n<td>Oscillates violently<\/td>\n<td>Gain too high for the speed<\/td>\n<td>Lower the gain, or lower the speed<\/td>\n<\/tr>\n<tr>\n<td>Drifts off on curves<\/td>\n<td>Gain too low<\/td>\n<td>Raise it until a weave appears<\/td>\n<\/tr>\n<tr>\n<td>Follows straights, fails corners<\/td>\n<td>Speed too high for the radius<\/td>\n<td>Slow down, or widen the corner<\/td>\n<\/tr>\n<tr>\n<td>Wheels hum, robot does not move<\/td>\n<td>Below the starting duty cycle<\/td>\n<td>The kick in the sketch; check <code>MINIMUM_SPEED<\/code><\/td>\n<\/tr>\n<tr>\n<td>Only full speed, no steering<\/td>\n<td><code>ENA<\/code>\/<code>ENB<\/code> jumpers still fitted<\/td>\n<td>Remove them; they must be on PWM pins<\/td>\n<\/tr>\n<tr>\n<td>Resets when it bumps something<\/td>\n<td>Stall current, near 1 A each<\/td>\n<td>A supply that holds up, grounds joined<\/td>\n<\/tr>\n<tr>\n<td>Drives straight off the card<\/td>\n<td><code>NO_LINE<\/code> treated as centred<\/td>\n<td>They are separate answers for this reason<\/td>\n<\/tr>\n<tr>\n<td>One channel never changes<\/td>\n<td>That head\u2019s height, then its trimmer<\/td>\n<td>Height first; all four must match<\/td>\n<\/tr>\n<tr>\n<td>All four always read the same<\/td>\n<td>Too close to the floor<\/td>\n<td>The emitter is flooding the receiver; raise them<\/td>\n<\/tr>\n<tr>\n<td>Erratic outdoors or by a window<\/td>\n<td>Infrared in daylight<\/td>\n<td>Shade it; these are indoor parts<\/td>\n<\/tr>\n<tr>\n<td>Worse as the batteries drain<\/td>\n<td>Expected<\/td>\n<td>Fresh cells, and re-check the gain<\/td>\n<\/tr>\n<tr>\n<td>Loses the line on the same corner every time<\/td>\n<td>Array too narrow for that radius<\/td>\n<td>Space the heads wider<\/td>\n<\/tr>\n<tr>\n<td>Works on the stand, not on the floor<\/td>\n<td>Weight and friction change everything<\/td>\n<td>Re-tune on the floor; the stand only proves direction<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Sources and attribution<\/h2>\n<p>All text, wiring guidance and code in this project are original Siratek work.<\/p>\n<ul>\n<li>Sensor behaviour, the separate heads, the working height, the <code>DT<\/code> and <code>DR<\/code> marking, the weighted position number and the <code>NO_LINE<\/code> distinction are as set out in <em>A Four-Way Line Tracking Sensor<\/em>, read from the Siratek product photographs for item 2943.<\/li>\n<li>Driver wiring, the enable jumpers, the PWM pin requirement and the bridge voltage drop are as set out in <em>Driving DC Motors with an L298N<\/em>.<\/li>\n<li>The motor mismatch, the stall current near 1 A, the starting duty cycle and the spare shaft for encoders are as set out in <em>Geared Motors and Wheels for a Robot<\/em>. <strong>Product 7406 is one motor and wheel; this project needs two.<\/strong><\/li>\n<li>The proportional controller, the pin reassignment to <code>A0<\/code>\u2013<code>A3<\/code>, the search behaviour and the <code>MINIMUM_SPEED<\/code> handling are Siratek\u2019s own design decisions for this project.<\/li>\n<\/ul>\n<p>Gain, base speed and the starting duty cycle depend on the chassis, the surface and the batteries, so the tutorial gives a tuning procedure rather than values to copy. <strong>This project has not yet been built and run on hardware.<\/strong> No third-party tutorial text, diagrams, photographs or code has been reproduced here.<\/p>\n<p><\/p>\n<\/body>","protected":false},"excerpt":{"rendered":"<p>A robot that follows a line of black tape across the floor. Four sensors looking down, two geared motors, and a sketch that turns &quot;the line is slightly to my left&quot; into &quot;slow the left wheel&quot;.<\/p>\n","protected":false},"featured_media":0,"template":"","siratek_tutorial_level":[],"class_list":["post-52287","siratek_tutorial","type-siratek_tutorial","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Project, a Line Following Robot - siratek<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Project, a Line Following Robot - siratek\" \/>\n<meta property=\"og:description\" content=\"A robot that follows a line of black tape across the floor. Four sensors looking down, two geared motors, and a sketch that turns &quot;the line is slightly to my left&quot; into &quot;slow the left wheel&quot;.\" \/>\n<meta property=\"og:url\" content=\"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/\" \/>\n<meta property=\"og:site_name\" content=\"siratek\" \/>\n<meta property=\"og:image\" content=\"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/line-following-robot-wiring.svg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/\",\"url\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/\",\"name\":\"Project, a Line Following Robot - siratek\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/siratek.net\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/#primaryimage\"},\"image\":{\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/siratek.net\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/line-following-robot-wiring.svg\",\"datePublished\":\"2026-11-01T10:50:51+00:00\",\"breadcrumb\":{\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/#primaryimage\",\"url\":\"https:\\\/\\\/siratek.net\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/line-following-robot-wiring.svg\",\"contentUrl\":\"https:\\\/\\\/siratek.net\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/line-following-robot-wiring.svg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/line-following-robot\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/siratek.net\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorials\",\"item\":\"https:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Project, a Line Following Robot\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/siratek.net\\\/#website\",\"url\":\"https:\\\/\\\/siratek.net\\\/\",\"name\":\"siratek\",\"description\":\"Your Best Electronic Retail Shop\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/siratek.net\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Project, a Line Following Robot - siratek","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/","og_locale":"en_US","og_type":"article","og_title":"Project, a Line Following Robot - siratek","og_description":"A robot that follows a line of black tape across the floor. Four sensors looking down, two geared motors, and a sketch that turns &quot;the line is slightly to my left&quot; into &quot;slow the left wheel&quot;.","og_url":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/","og_site_name":"siratek","og_image":[{"url":"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/line-following-robot-wiring.svg","type":"","width":"","height":""}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/","url":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/","name":"Project, a Line Following Robot - siratek","isPartOf":{"@id":"https:\/\/siratek.net\/#website"},"primaryImageOfPage":{"@id":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/#primaryimage"},"image":{"@id":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/#primaryimage"},"thumbnailUrl":"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/line-following-robot-wiring.svg","datePublished":"2026-11-01T10:50:51+00:00","breadcrumb":{"@id":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/#primaryimage","url":"https:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/line-following-robot-wiring.svg","contentUrl":"https:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/line-following-robot-wiring.svg"},{"@type":"BreadcrumbList","@id":"http:\/\/siratek.net\/index.php\/tutorials\/line-following-robot\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/siratek.net\/"},{"@type":"ListItem","position":2,"name":"Tutorials","item":"https:\/\/siratek.net\/index.php\/tutorials\/"},{"@type":"ListItem","position":3,"name":"Project, a Line Following Robot"}]},{"@type":"WebSite","@id":"https:\/\/siratek.net\/#website","url":"https:\/\/siratek.net\/","name":"siratek","description":"Your Best Electronic Retail Shop","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/siratek.net\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"}]}},"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial\/52287","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial"}],"about":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/types\/siratek_tutorial"}],"version-history":[{"count":0,"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial\/52287\/revisions"}],"wp:attachment":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/media?parent=52287"}],"wp:term":[{"taxonomy":"siratek_tutorial_level","embeddable":true,"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial_level?post=52287"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}