Example Code for MiniQ 2WD PLUS & MiniQ 2WD Dual-Board Robotic System
Last revision 2026/01/19
This document explains how to program the MiniQ 2WD PLUS (host) and MiniQ 2WD (slave) dual-board robot over I²C. It includes setup steps and separate sketches to enable menu control, RGB/buzzer effects, battery monitoring, and four driving modes.
Introduction
The MiniQ 2WD PLUS and MiniQ 2WD form an integrated dual-board intelligent robot system designed for education and maker projects.
- MiniQ 2WD PLUS acts as the host controller, handling user interaction, command parsing, and mode selection.
- MiniQ 2WD serves as the execution unit (slave), managing motor control, sensor reading, and physical actions.
The two boards are physically connected via a Gadgeteer interface and communicate using the I²C (IIC) protocol. The PLUS receives commands from:
- The 5-way analog joystick on the LCD12864 Shield,
- An infrared remote control, or
- The three onboard calibration buttons (KEY1–KEY3) on the 2WD base,
and then forwards corresponding instructions to the 2WD over I²C.
The system supports four core movement modes:
- Patrol Line Mode: Tracks a black line using five infrared sensors; thresholds are adjustable for different surfaces.
- Light Hunter Mode: Seeks bright light sources using an ambient light sensor.
- Obstacle Avoidance Mode: Uses IR transceivers to detect and avoid obstacles.
- Remote Control Mode: Allows manual driving via the included IR remote.
Additional features include RGB lighting effects, buzzer melodies, and real-time battery monitoring, making it a complete educational robotics platform.
Hardware Preparation
- MiniQ 2WD PLUS main controller board ×1
- MiniQ 2WD chassis (with motors, sensors, RGB LEDs, and buzzer) ×1
- LCD12864 Shield for Arduino (with integrated 5-way analog joystick) ×1
- Gadgeteer cable (for connecting PLUS and 2WD) ×1
- Infrared remote control (included in the MiniQ 2WD kit) ×1
- USB cables (for programming both boards separately) ×2
- Power source: Recommended 6V–9V battery pack connected to the 2WD chassis
⚠️ The two boards must be programmed with separate sketches—do not upload the same code to both.
Software Preparation
-
Arduino IDE (version 1.8.0 or higher recommended)
-
Install the following libraries:
- JLX12864G Library (for LCD12864 display control)
? Download here
→ Extract and place in your Arduinolibrariesfolder. - Adafruit_NeoPixel Library (for WS2812B RGB LED control)
→ Install via Tools > Manage Libraries... - IRremote Library (for infrared signal decoding)
→ Install via Library Manager (recommended version: IRremote by Armin Joachimsmeyer)
- JLX12864G Library (for LCD12864 display control)
-
Select the correct board (Arduino Uno) in the IDE before uploading.
Wiring Diagram
The system is modular and requires no external wiring:
-
The MiniQ 2WD PLUS and MiniQ 2WD are connected directly via the standard Gadgeteer interface, which internally integrates:
- I²C lines (SCL/SDA)
- Power (VCC/GND)
- UART (for optional wireless modules)
-
The LCD12864 Shield plugs directly onto the MiniQ 2WD PLUS, using digital pins 8–13 and analog pin A0 (for the joystick).
-
All peripherals—including IR sensors, motor drivers, RGB LEDs, buzzer, and buttons—are pre-soldered on the MiniQ 2WD chassis.
Simply snap the two boards together using the Gadgeteer connector and attach the LCD shield to get started.
sample code for PLUS
Please download LCD library first.
#include<Wire.h>
#include<JLX12864G.h>
JLX12864G lcd(8,9,10,11,13);
long TimeNum=0;
char SendCommandData[]={'S','M','L','A','E','O','R','#'};//
char xReadData=0;
int Key_Num=0;
int Key_Up=0,Key_Down=0,Key_Left=0,Key_Right=0,Key_Ok=0;
byte CheckData1[]={1,3,5};
byte CheckData2[]={1,3,5,7};
char dfrobotlogo[]={
0x08,0xF8,0xF8,0x08,0x18,0xF0,0xE0,0x00,0x08,0x0F,0x0F,0x08,0x0C,0x07,0x03,0x00,
0x08,0xF8,0xF8,0x88,0xC8,0xC8,0x18,0x10,0x08,0x0F,0x0F,0x08,0x01,0x01,0x00,0x00,
0x08,0xF8,0xF8,0xC8,0xC8,0x78,0x30,0x00,0x08,0x0F,0x0F,0x08,0x03,0x0F,0x0C,0x08,
0xE0,0xF0,0x18,0x08,0x18,0xF0,0xE0,0x00,0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,
0x08,0xF8,0xF8,0x48,0x48,0xF8,0xB0,0x00,0x08,0x0F,0x0F,0x08,0x08,0x0F,0x07,0x00,
0xE0,0xF0,0x18,0x08,0x18,0xF0,0xE0,0x00,0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,
0x18,0x18,0x08,0xF8,0xF8,0x08,0x18,0x18,0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
};
char check[]={0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,}; //It is a function switch indicator
char Spacebar[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
char FullPower[]={0xFF,0x01,0xFD,0xFD,0xFD,0xFD,0x01,0xFF,0xFF,0x80,0xBF,0xBF,0xBF,0xBF,0x80,0xFF,};//It is a full power indicator
char HalfPower[]={0xFF,0x01,0x81,0x81,0x81,0x81,0x01,0xFF,0xFF,0x80,0xBF,0xBF,0xBF,0xBF,0x80,0xFF,};//
char LowPower[]={0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x80,0xB8,0xB8,0xB8,0xB8,0x80,0xFF,};
char ShortagePower[]={0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0xFF,};
void setup()
{
pinMode(7,OUTPUT);
lcd.init();
lcd.clear();
Wire.begin();
Serial1.begin(57600);
}
void loop()
{
int n=0;
lcd.graphic_8x16(3,2,dfrobotlogo,7);
lcd.string_5x7(5,5,"miniQ 2WD V3");
lcd.string_5x7(6,15,"www.dfrobot.com");
lcd.string_5x7(8,1,"Please press any key!");
digitalWrite(7,HIGH);
while(Key_Num==0)
{
Key_Scan();
}
lcd.clear();
FunctionMenu();
while(1)//Enter the cycle does not return
{
Key_Scan();
Key_Deal();
if(Key_Down==4)
{
Key_Down=1;
}
ReadPowerData();
}
}
void FunctionMenu(void)
{
lcd.string_5x7(2,9,"MusicFunction");
lcd.string_5x7(4,9,"LightsFunction");
lcd.string_5x7(6,9,"MovementFunction");
}
void MusicFunctionMode(void)
{
lcd.string_5x7(2,28,"MusicFunction");
}
void LightFunctionMode(void)
{
lcd.string_5x7(2,24,"LightsFunction");
}
void MovementFunctionMode(void)
{
lcd.string_5x7(2,9,"PatrolLine");
lcd.string_5x7(4,9,"SearchLight");
lcd.string_5x7(6,9,"ObstacleAvoidance");
lcd.string_5x7(8,9,"RemoteControl");
}
void Key_Scan(void)
{
int analog = analogRead(A0);
if(analog>=950)
{
Key_Num=0;
return;
}
else
{
Key_Num=1;
if(analog>=750&&analog<950)//Press up and key_num=1
{
delay(2);
if(analog>=750&&analog<950)
{
while(analog<950)
analog = analogRead(A0);
Key_Up++;
}
}
else if(analog>=550&&analog<750)//Press right and key_num=2
{
delay(2);
if(analog>=550&&analog<750)
{
while(analog<950)
analog = analogRead(A0);
Key_Right=1;
}
}
else if(analog>=350&&analog<550)//Press down and key_num=3
{
delay(2);
if(analog>=350&&analog<550)
{
while(analog<950)
analog = analogRead(A0);
Key_Down++;
}
}
else if(analog>=150&&analog<350)//Press ok and key_num=4
{
delay(2);
if(analog>=150&&analog<350)
{
while(analog<950)
analog = analogRead(A0);
Key_Ok=1;
}
}
else if(analog<150)//Press left and key_num=5
{
delay(2);
if(analog<150)
{
while(analog<950)
analog = analogRead(A0);
Key_Left=1;
}
}
}
}
void Key_Deal(void)
{
if(Key_Down==1)
{
Lcd_Check_Graphic(1,0);
if(Key_Ok==1)// enter music fuction menu
{
lcd.clear();
MusicFunctionMode();
IICWritData(SendCommandData[1]);
while(1)
{
Key_Scan();
Key_Ok=0;
if(Key_Left==1)
{
Key_Left=0;
Key_Down=1;
IICWritData(SendCommandData[0]);
goto Back_One_Level;//back to main menu
}
ReadPowerData();
}
}
}
if(Key_Down==2)
{
Lcd_Check_Graphic(3,0);
if(Key_Ok==1)//enter light function menu
{
lcd.clear();
LightFunctionMode();
IICWritData(SendCommandData[2]);
while(1)
{
Key_Scan();
Key_Ok=0;
if(Key_Left==1)
{
Key_Left=0;
Key_Down=2;
IICWritData(SendCommandData[0]);
goto Back_One_Level;//back to main menu
}
ReadPowerData();
}
}
}
if(Key_Down==3)
{
Lcd_Check_Graphic(5,0);
if(Key_Ok==1)//enter movement function menu
{
Key_Ok=0;
Key_Down=1;
lcd.clear();
MovementFunctionMode();
while(1)
{
Key_Scan();
if(Key_Left==1)
{
Key_Left=0;
Key_Down=3;
goto Back_One_Level;
}
if(Key_Down==1)
{
Lcd_Check_Graphic(1,1);
if(Key_Ok==1)
{
Key_Ok=0;
Key_Up=0;
lcd.clear();
lcd.string_5x7(2,20,"PatrolLineModel");//enter patrol line model
// lcd.graphic_16x16(3,1,StartData,2);
IICWritData(SendCommandData[3]);
//send line data
while(1)
{
Key_Scan();
if(Key_Left==1)
{
Key_Ok=0;
Key_Left=0;
Key_Down=1;
IICWritData(SendCommandData[0]);
goto Back_Two_Level_Move;//back to second-level menu
}
ReadPowerData();
}
Back_Two_Level_Move://second-level menu
lcd.clear();
MovementFunctionMode();
}
}
if(Key_Down==2)
{
Lcd_Check_Graphic(3,1);
if(Key_Ok==1)
{
Key_Ok=0;
lcd.clear();
lcd.string_5x7(2,17,"SearchLightModel");
IICWritData(SendCommandData[4]);
//send light data
while(1)
{
Key_Scan();
if(Key_Left==1)
{
Key_Ok=0;
Key_Left=0;
Key_Down=2;
IICWritData(SendCommandData[0]);
goto Back_Two_Level_Move;//back to second-level menu
}
ReadPowerData();
}
}
}
if(Key_Down==3)
{
Lcd_Check_Graphic(5,1);
if(Key_Ok==1)
{
Key_Ok=0;
lcd.clear();
lcd.string_5x7(2,2,"ObstacleAvoidanceModel");
IICWritData(SendCommandData[5]);
//send OBSTACLEAVOIDANCE data
while(1)
{
Key_Scan();
if(Key_Left==1)
{
Key_Ok=0;
Key_Left=0;
Key_Down=3;
IICWritData(SendCommandData[0]);
goto Back_Two_Level_Move;//back to second-level menu
}
ReadPowerData();
}
}
}
if(Key_Down==4)
{
Lcd_Check_Graphic(7,1);
if(Key_Ok==1)
{
Key_Ok=0;
lcd.clear();
lcd.string_5x7(2,12,"RemoteControlModel");
IICWritData(SendCommandData[6]);
//send Remote data
while(1)
{
Key_Scan();
if(Key_Left==1)
{
Key_Ok=0;
Key_Left=0;
Key_Down=4;
IICWritData(SendCommandData[0]);
goto Back_Two_Level_Move;//back to second-level menu
}
SerialReadWriteData();
ReadPowerData();
}
}
}
if(Key_Down==5)
Key_Down=1;
ReadPowerData();
}
Back_One_Level://back to main menu
lcd.clear();
FunctionMenu();
}
}
}
void SerialReadWriteData(void)
{
if(Serial1.available())
{
char SerialData = Serial1.read();
switch(SerialData)
{
case 'w':
IICWritData('w');
break;
case 'a':
IICWritData('a');
break;
case 's':
IICWritData('s');
break;
case 'd':
IICWritData('d');
break;
default:
IICWritData('S');
break;
}
}
}
void Lcd_Check_Graphic(byte CheckNum,char RowsNum)//The check of indicator switch function
{
if(RowsNum==0)
{
for(int i=0;i<3;i++)
{
if(CheckData1[i]==CheckNum)
lcd.graphic_8x16(CheckNum,1,check,1);
else
lcd.graphic_8x16(CheckData1[i],1,Spacebar,1);
}
}
else if(RowsNum==1)
{
for(int j=0;j<4;j++)
{
if(CheckData2[j]==CheckNum)
lcd.graphic_8x16(CheckNum,1,check,1);
else
lcd.graphic_8x16(CheckData2[j],1,Spacebar,1);
}
}
}
void IICWritData(char data)
{
Wire.beginTransmission(4); //send data to the slave named 4
Wire.write(data); // send a byte in variable data
Wire.endTransmission(); // stop sending
}
void IICReadData(void)
{
Wire.requestFrom(4, 1); //request the slave to upload a byte
while(Wire.available()>0) // when data sent from the slave to the host
{
xReadData = Wire.read(); //assign a value to 'xReadData'
}
}
// electric quantity reading function
void ReadPowerData(void)
{
TimeNum++;
if(TimeNum==10)
{
TimeNum=0;
IICReadData();
switch(xReadData)
{
case'3':
{
lcd.graphic_8x16(1,121,FullPower,1);
digitalWrite(7,HIGH);
break;
}
case'2':
{
lcd.graphic_8x16(1,121,HalfPower,1);
digitalWrite(7,HIGH);
break;
}
case'1':
{
lcd.graphic_8x16(1,121,LowPower,1);
digitalWrite(7,HIGH);
break;
}
case'0':
{
lcd.graphic_8x16(1,121,ShortagePower,1);
digitalWrite(7,LOW);
break;
}
}
}
}
sample code for 2WD
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <IRremote.h>
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args,BYTE);
#endif
uint8_t empty[8] = {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f};
uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0,0x0};
#define address 0x1e
int length;
#define RGB_ws2812 10
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, RGB_ws2812, NEO_GRB + NEO_KHZ800);
#define EN1 6//pin for run the right motor
#define IN1 7//pin for control right motor direction
#define EN2 5//pin for run the left motor
#define IN2 12//pin for control left motor direction
#define FORW 0//go frword
#define BACK 1//go back
#define IR_IN 17//IR receiver pin
#define L_IR 13//left ir transmitter pin
#define R_IR 8//right ir transmitter pin
#define BUZZER 16//buzzer pin
#define Vr 5// Voltage reference
#define NTD0 -1
#define NTD1 294
#define NTD2 330
#define NTD3 350
#define NTD4 393
#define NTD5 441
#define NTD6 495
#define NTD7 556
#define NTDL1 147
#define NTDL2 165
#define NTDL3 175
#define NTDL4 196
#define NTDL5 221
#define NTDL6 248
#define NTDL7 278
#define NTDH1 589
#define NTDH2 661
#define NTDH3 700
#define NTDH4 786
#define NTDH5 882
#define NTDH6 990
#define NTDH7 112
//define all the keys' frequency of D major
#define WHOLE 1
#define HALF 0.5
#define QUARTER 0.25
#define EIGHTH 0.25
#define SIXTEENTH 0.625
//define all the beats
int tune1[]= //list keys according to music
{
NTDH3,NTDH3,NTDH2,NTDH3,NTD6,NTDH3,NTD6,NTD5,
NTDH2,NTDH1,NTDH2,NTDH3,NTDH2,NTDH1,
NTD6,NTDH3,NTDH2,NTDH3,NTDH2,NTDH1,NTD7,
NTD6,NTD5,NTD6,NTD7,NTD6,NTD5
};
float durt1[]= //Corresponding beats according to key list
{
0.5,0.5,1,0.5,1,1.5,1,1,
0.5,0.5,1,0.5,1,2,
1,1,0.5,0.5,1,0.5,1,
0.5,0.5,1,0.5,1,2
};
int tonepin=16;
char xReadWriteData='S';
int count;////count the motor speed pulse
float data[7]={0X00,0X00,0X00,0X00,0x00,0xff,0x00};//store 8-channel A/D converter value
char value[5]={0x00,0x00,0x00,0x00,0x00};//store the value read from the sensors
char key_1=0x00,key_2=0x00,key_3=0x00;//count of 3 keys
//count mileage
int count_r=0,count_l=0;//count pluse return by teo wheel
//remote parameter
IRrecv irrecv(IR_IN);
decode_results results;
int SpeedNumL=100;
int SpeedNumR=100;
void Motor_Control(int M1_DIR,int M1_EN,int M2_DIR,int M2_EN)//control motor
{
//////////M1////////////////////////
if(M1_DIR==FORW)//M1 motor direction
digitalWrite(IN1,FORW); //forward
else
digitalWrite(IN1,BACK);//back
if(M1_EN==0)
analogWrite(EN1,LOW);//stop
else
analogWrite(EN1,M1_EN);//set speed
///////////M2//////////////////////
if(M2_DIR==FORW) //M2 motor direction
digitalWrite(IN2,FORW); //forward
else
digitalWrite(IN2,BACK);///back
if(M2_EN==0)
analogWrite(EN2,LOW);//stop
else
analogWrite(EN2,M2_EN);//set speed
}
//avoidance
void L_Send40KHZ(void)//left ir transmitter sends 40kHZ pulse
{
int i;
for(i=0;i<24;i++)
{
digitalWrite(L_IR,LOW);
delayMicroseconds(8);
digitalWrite(L_IR,HIGH);
delayMicroseconds(8);
}
}
void R_Send40KHZ(void)//right ir transmitter sends 40kHZ pulse
{
int i;
for(i=0;i<24;i++)
{
digitalWrite(R_IR,LOW);
delayMicroseconds(8);
digitalWrite(R_IR,HIGH);
delayMicroseconds(8);
}
}
//mileage counting
void LEFT(void)
{
//pluse count return by two wheels
//if(++count_l=100)
count_l++;
}
void RIGHT(void)
{
//pluse count return by right side wheel
// if(++count_r=100)
count_r++;
}
void pcint0_init(void)//initial the interrupt
{
PCICR = 0X01;//enable interrupt when pins in group 0 changes
PCMSK0 = 0X01;//enable interrupt when pin 0 in group 0 changes
}
ISR(PCINT0_vect)//motor encoder interrupt
{
count++;
}
void Obstacle_Avoidance(void)
{
char i;
count=0;
for(i=0;i<20;i++)//left transmitter sends 20 pulses
{
L_Send40KHZ();
delayMicroseconds(600);
}
if(count>20)//if recieved a lot pulse , it means there's a obstacle
{
Motor_Control(BACK,SpeedNumR,BACK,SpeedNumL);
delay(50);
Motor_Control(BACK,SpeedNumR,FORW,SpeedNumL);
delay(50);
}
else
{
Motor_Control(FORW,SpeedNumR,FORW,SpeedNumL);
}
count=0;
for(i=0;i<20;i++)//right transmitter sends 20 pulses
{
R_Send40KHZ();
delayMicroseconds(600);
}
if(count>20)
{
Motor_Control(BACK,SpeedNumR,BACK,SpeedNumL);
delay(50);
Motor_Control(FORW,SpeedNumR,BACK,SpeedNumL);
delay(50);
}
else
{
Motor_Control(FORW,SpeedNumR,FORW,SpeedNumL);
}
}
//read volt value in analog port
void Read_Value(void)
{
int i;
for(i=0;i<7;i++)
{
data[i]=analogRead(i);//store the value read from the analog port
data[i]= ((data[i]*Vr)/1024); //turn to analog value
}
}
//line follower
void value_adjust(unsigned char num)//set sensor parameter
{
if(num==1)//set the first sensor
{
if(data[0]>value[0])
{
colorWipe(strip.Color(0, 255, 0), 5); // Red
}
else
{
colorWipe(strip.Color(255, 0, 0), 1); // Green
}
}
if(num==2)//set the second sensor
{
if(data[1]>value[1])
{
colorWipe(strip.Color(0, 255, 0), 1); // Red
}
else
{
colorWipe(strip.Color(255, 0, 0), 1); // Green
}
}
if(num==3)//set the third sensor
{
if(data[2]>value[2])
{
colorWipe(strip.Color(0, 255, 0), 1); // Red
}
else
{
colorWipe(strip.Color(255, 0, 0), 1); // Green
}
}
if(num==4)//set the forth sensor
{
if(data[3]>value[3])
{
colorWipe(strip.Color(0, 255, 0), 1); // Red
}
else
{
colorWipe(strip.Color(255, 0, 0), 1); // Green
}
}
if(num==5)//set the fifth sensor
{
if(data[4]>value[4])
{
colorWipe(strip.Color(0, 255, 0), 1); // Red
}
else
{
colorWipe(strip.Color(255, 0, 0), 1); // Green
}
}
}
void huntline_deal(void)
{
if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]<(value[2])&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(FORW,100,FORW,100);//go forword
else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]<(value[2]-1)&&data[3]<(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(BACK,20,FORW,100);//turn right
else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]<(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(BACK,100,FORW,100);//fast turn right
else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]<(value[3]-1)&&data[4]<(value[4]-1))
Motor_Control(BACK,100,FORW,100);//fast turn right
else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]<(value[4]-1))
Motor_Control(BACK,100,FORW,100);//fast turn right
else if(data[0]>(value[0]-1)&&data[1]<(value[1]-1)&&data[2]<(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(FORW,100,BACK,20);//turn left
else if(data[0]>(value[0]-1)&&data[1]<(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(FORW,100,BACK,100);//fast turn left
else if(data[0]<(value[0]-1)&&data[1]<(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(FORW,100,BACK,100);//fast turn left
else if(data[0]<(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
Motor_Control(FORW,100,BACK,100);//fast turn left
}
//light hunter
void hunt_light(void)
{
if (data[5]>3.5) //measure from present environment
Motor_Control(BACK,SpeedNumR,FORW,SpeedNumL);//turn right
else if (data[5]< 1.5)
Motor_Control(FORW,SpeedNumR,BACK,SpeedNumL);//
else
Motor_Control(FORW,0,FORW,0);//stop
}
//remote
void dump(decode_results *results)
{
if(results->value==0x00fd00ff)
{
Motor_Control(FORW,0,FORW,0);
}
if(results->value==0x00fd807f)
{
Motor_Control(FORW,100,FORW,100);
}
if(results->value==0x00fd906f)
{
Motor_Control(BACK,100,BACK,100);
}
if(results->value==0x00fd20df)
{
Motor_Control(FORW,100,BACK,100);//turn left
}
if(results->value==0x00fd609f)
{
Motor_Control(BACK,100,FORW,100);//turn right
}
}
//buzzer function
void buzzer(void)
{
digitalWrite(BUZZER,HIGH);//buzzer sound
delay(1);
digitalWrite(BUZZER,LOW);//
delay(10);
}
//key scan
void key_scan(void)
{
if(data[6]>4.50&&data[6]<6.00)//without button
return;
else
{
if(data[6]>=0.00&&data[6]<0.80)//push key 1
{
delay(10);//Debounce delay
if(data[6]>=0.00&&data[6]<0.80)//do push key 1
{
buzzer();
while(data[6]>=0.00&&data[6]<0.80)
Read_Value();
key_1++;//count key 1
if(key_1>=1&&key_1<=5)
value_adjust(key_1);//adjust sensor parameter
}
}
else if(data[6]>=0.80&&data[6]<3)//push key 2
{
delay(10);
if(data[6]>=0.80&&data[6]<3)
{
buzzer();
while(data[6]>=0.50&&data[6]<3)
Read_Value();
if(key_1>=1&&key_1<=5)//count of key 1 range in 1 to 5
{
value[key_1-1]++;//the limit where sensor can distinguish do the "++" count
value_adjust(key_1);//adjust sensor parameter
}
else
{
key_2++;//count key 2
}
}
}
else if(data[6]>=3&&data[6]<4)
{
delay(10);
if(data[6]>=3&&data[6]<4)
{
buzzer();
while(data[6]>=3&&data[6]<4)
Read_Value();
if(key_1>=1&&key_1<=5)
{
value[key_1-1]--;//the limit where sensor can distinguish do the "--" count
value_adjust(key_1);
}
else
{
key_3++;
}
}
}
}
}
void key_deal()// hunt line
{
if(key_1==6)
{
huntline_deal();
key_2=0x00;
key_3=0x00;
}
else if(key_1 == 7)
{
key_1 = 0;
Motor_Control(FORW,0,FORW,0);//stop
}
}
//low voltage check
void low_voltage_check(void)
{
float voltage_num=analogRead(A9);
voltage_num=(15*voltage_num)/2048;
if(voltage_num<4.0||voltage_num>7.0)
{
while(1)
{
voltage_num=analogRead(A9);
voltage_num=(15*voltage_num)/2048;
if(voltage_num>=4.0&&voltage_num<=7.0)
break;
buzzer();
colorWipe(strip.Color(0, 255, 0), 1); // Red
colorWipe(strip.Color(0, 0, 0), 1);
}
}
}
void Velocity_function(void)//speed management
{
if(count_l>count_r)
{
SpeedNumL=SpeedNumL-1;
SpeedNumR=SpeedNumR+1;
}
else if(count_l<count_r)
{
SpeedNumL=SpeedNumL+1;
SpeedNumR=SpeedNumR-1;
}
count_l=0;
count_r=0;
}
void colorWipe(uint32_t c, uint8_t wait)
{
for(uint16_t i=0; i<strip.numPixels(); i++)
{
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void setup()
{
pinMode(5,OUTPUT);//init the motor driver pins
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
pinMode(12,OUTPUT);
pinMode(8,OUTPUT);
pinMode(10,OUTPUT);
pinMode(13,OUTPUT);
pinMode(14,OUTPUT);
pinMode(16,OUTPUT);
pinMode(17,INPUT);
irrecv.enableIRIn();
strip.begin();
strip.show();
length=sizeof(tune1)/sizeof(tune1[0]); //calculate length
Wire.begin(4);//name the slave with 4
Wire.onReceive(receiveEvent);//login the event slave received
Wire.onRequest(requestEvent);// login the event host requested
attachInterrupt(2,RIGHT,FALLING);
attachInterrupt(3,LEFT,FALLING);
Motor_Control(FORW,0,FORW,0);//stop motor
}
void loop()
{
switch(xReadWriteData)
{
case'S':
Motor_Control(FORW,0,FORW,0);
break;
case'M':
MusicFunction();
break;
case'L':
RGBFunction();
break;
case'A':
LineFunction();
break;
case'E':
LightFuntion();
break;
case'O':
ObstacleFunction();
break;
case'R':
RemoteFunction();
break;
case'w':
Motor_Control(FORW,SpeedNumR,FORW,SpeedNumL);//go forword
break;
case'a':
Motor_Control(FORW,SpeedNumR,BACK,SpeedNumL);//turn left
break;
case's':
Motor_Control(BACK,SpeedNumR,BACK,SpeedNumL);//go back
break;
case'd':
Motor_Control(BACK,SpeedNumR,FORW,SpeedNumL);//turn right
break;
}
Velocity_function();
// low_voltage_check();
colorWipe(strip.Color(0, 0, 0), 1);
}
// do when slave receive data
void receiveEvent(int howMany)
{
while( Wire.available()>1) // do loop until the data packet have the last byte
{
char c = Wire.read(); // receive data as a byte
}
//receive the last data
xReadWriteData = Wire.read(); // receive data as integer
}
//do when the event host requested
void requestEvent()
{
float analog = analogRead(A9);
analog =(15*analog)/2048;
if(analog>4.7)
Wire.write( '3');
else if(analog>4.3)
Wire.write( '2');
else if(analog>3.9)
Wire.write( '1');
else
Wire.write( '0');
// response and send a byte of data to the host
}
uint32_t Wheel(byte WheelPos)
{
if(WheelPos < 85)
{
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
else if(WheelPos < 170)
{
WheelPos -= 85;
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else
{
WheelPos -= 170;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
void rainbow(uint8_t wait)
{
uint16_t i, j;
for(j=0; j<256; j++)
{
for(i=0; i<strip.numPixels(); i++)
{
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait)
{
uint16_t i, j;
for(j=0; j<256*5; j++)
{ // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++)
{
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
void MusicFunction(void)//
{
for(int x=0;x<length;x++)
{
tone(tonepin,tune1[x]);
delay(350*durt1[x]); //beat delay accroding to the music
noTone(tonepin);
}
}
void RGBFunction(void)//
{
rainbow(5);
rainbowCycle(5);
}
void LineFunction(void)//
{
//buzzer();
Read_Value();
key_scan();
key_deal();
}
void LightFuntion(void)//
{
Read_Value();
hunt_light();
Velocity_function();
}
void ObstacleFunction(void)//
{
pcint0_init();//initial the interrupt
sei(); //enable global interrupt
Obstacle_Avoidance();
Velocity_function();
}
void RemoteFunction(void)
{ Motor_Control(FORW,0,FORW,0);//run motor
while(1)
{
if(irrecv.decode(&results))
{
dump(&results);
irrecv.resume();
}
}
}
Result
When powered on:
The LCD displays a welcome screen prompting the user to press any key.
The user navigates through a hierarchical menu:
Music: Plays a melody via the buzzer.
Lights: Runs rainbow and cycle RGB effects.
Movement: Selects one of four autonomous/manual modes.
In Patrol Line Mode, the user can calibrate all five line sensors using KEY1–KEY3, with real-time RGB feedback (green = on line, red = off).
In Remote Mode, the car responds instantly to the IR remote’s directional keys.
The battery level is continuously monitored and displayed as an icon on the LCD.
All actions are executed smoothly by the 2WD base, demonstrating reliable I²C communication, sensor fusion, and motor control.
This system provides a comprehensive, hands-on platform for learning embedded systems, robotics, and human-machine interaction.
Was this article helpful?
