#include <Arduino.h>
#include <EEPROM.h>
#include <avr/wdt.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(9, 10); // CE, CSN
const byte address[6] = "00001"; //Choose a unique adress (should be the same on the receiver side)
/*
U8g2lib Example Overview:
Frame Buffer Examples: clearBuffer/sendBuffer. Fast, but may not work with all Arduino boards because of RAM consumption
Page Buffer Examples: firstPage/nextPage. Less RAM usage, should work with all Arduino boards.
U8x8 Text Only Example: No RAM usage, direct communication with display controller. No graphics, 8x8 Text only.
*/
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE); // Uncomment for arduino MEGA2560
//U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* CS=*/ 10, /* reset=*/ 8); // Uncomment for arduino UNO, NANO
const uint8_t spot_bitmap[] = {
0x00, 0x00, 0x7F, 0xF8, 0x00, // ############
0x00, 0x3F, 0x80, 0x74, 0x00, // ####### ### #
0x03, 0xE0, 0x00, 0x7A, 0x00, // ##### #### #
0x7C, 0x00, 0x00, 0x66, 0x00, // ##### ## ##
0x83, 0xFE, 0x03, 0xE2, 0x00, // # ######### ##### #
0x80, 0x1E, 0x07, 0x21, 0x00, // # #### ### # #
0xFF, 0x0F, 0x9C, 0x39, 0x00, // ######## ##### ### ### #
0xF4, 0x88, 0x70, 0x7C, 0x80, // #### # # # ### ##### #
0x6A, 0x48, 0x20, 0x8C, 0x80, // ## # # # # # # ## #
0x7A, 0x4F, 0x1F, 0x06, 0x40, // #### # # #### ##### ## #
0x35, 0x2F, 0x0C, 0x07, 0x20, // ## # # # #### ## ### #
0x1D, 0x59, 0x88, 0x03, 0xA0, // ### # # ## ## # ### #
0x07, 0xF0, 0xC4, 0x03, 0xA0, // ####### ## # ### #
0x03, 0xF0, 0xE2, 0x01, 0xE0, // ###### ### # ####
0x07, 0xC1, 0xF1, 0x07, 0xC0, // ##### ##### # #####
0x0F, 0x83, 0xF9, 0x0F, 0x80, // ##### ####### # #####
0x0F, 0x07, 0xB9, 0x1E, 0x00, // #### #### ### # ####
0x1C, 0x07, 0x9D, 0x1C, 0x00, // ### #### ### # ###
0x38, 0x03, 0x3E, 0x38, 0x00, // ### ## ##### ###
0x78, 0x00, 0x7E, 0x78, 0x00, // #### ###### ####
0xF0, 0x00, 0xF8, 0x78, 0x00, // #### ##### ####
0xF0, 0x01, 0xE0, 0x78, 0x00, // #### #### ####
0x70, 0x01, 0xC0, 0x30, 0x00, // ### ### ##
0x20, 0x03, 0x80, 0x00, 0x00, // # ###
0x00, 0x07, 0x00, 0x00, 0x00, // ###
0x00, 0x0F, 0x00, 0x00, 0x00, // ####
0x00, 0x0F, 0x00, 0x00, 0x00, // ####
0x00, 0x0F, 0x00, 0x00, 0x00, // ####
0x00, 0x06, 0x00, 0x00, 0x00, // ##
};
char pot1[4];
char pot2[4];
char pot3[4];
char pot4[4];
char pot5[4];
char pot6[4];
char gaitVersion[4];
char joyLX[4];
char joyLY[4];
char joyRX[4];
char joyRY[4];
int gait = 0;
int Array[13];
int LXmid;
int LYmid;
int LYup;
int LYdown;
int LXleft;
int LXright;
int RXmid;
int RYmid;
int RYup;
int RYdown;
int RXleft;
int RXright;
int LY;
int LX;
int RY;
int RX;
//WRITE CALIBRATIONS DATA TO THE EEPROM IN 2 BYTES
void EEPROMWriteInt(int address, int value)
{
byte two = (value & 0xFF);
byte one = ((value >> 8) & 0xFF);
EEPROM.update(address, two);
EEPROM.update(address + 1, one);
}
//READ CALOBRATION DATA FROM THE EEPROM IN 2 BYTE'S PER VARIABLE
int EEPROMReadInt(int address)
{
long two = EEPROM.read(address);
long one = EEPROM.read(address + 1);
return ((two << 0) & 0xFFFFFF) + ((one << 8) & 0xFFFFFFFF);
}
void writeDisplay(){
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_sirclivethebold_tr); // choose a suitable font
u8g2.drawStr(86,7,"Spot"); // write text to display
u8g2.drawBitmap( 88, 10, 5, 29, spot_bitmap); // write SpotMicro image to display
u8g2.setFont(u8g2_font_5x7_tr); // choose a suitable font u8g2_font_6x10_tf
if(digitalRead(7)== HIGH){
u8g2.drawStr(82,13,"A"); // Autonomous Mode
}else{
u8g2.drawStr(82,13,"M"); // Manual Control
}
if(digitalRead(6)==LOW){
u8g2.drawStr(82,21,"O"); // Servo's Powered
}else{
u8g2.drawStr(82,21,"X"); // Servo's Off
}
u8g2.drawStr(82,29,gaitVersion);
u8g2.setFont(u8g2_font_ncenB08_tr);
// LEFT JOYSTICK X axis Y axis
u8g2.drawDisc(34-(map(LX,LXright,LXleft,4,13)), map(LY,LYup,LYdown,4,13), 4);
u8g2.drawHLine(15, 0, 5);
u8g2.drawHLine(15, 34, 5);
u8g2.drawVLine(0, 15, 5);
u8g2.drawVLine(34, 15, 5);
u8g2.drawHLine(15, 5, 5);
u8g2.drawHLine(15, 29, 5);
u8g2.drawVLine(5, 15, 5);
u8g2.drawVLine(29, 15, 5);
u8g2.drawHLine(15, 10, 5);
u8g2.drawHLine(15, 24, 5);
u8g2.drawVLine(10, 15, 5);
u8g2.drawVLine(24, 15, 5);
u8g2.drawHLine(15, 15, 5);
u8g2.drawHLine(15, 19, 5);
u8g2.drawVLine(15, 15, 5);
u8g2.drawVLine(19, 15, 5);
//RIGHT JOYSTICK X axis Y axis
u8g2.drawDisc((34-(map(RX,RXright,RXleft,4,13)))+45, map(RY,RYup,RYdown,4,13), 4);
u8g2.drawHLine(60, 0, 5);
u8g2.drawHLine(60, 34, 5);
u8g2.drawVLine(45, 15, 5);
u8g2.drawVLine(79, 15, 5);
u8g2.drawHLine(60, 5, 5);
u8g2.drawHLine(60, 29, 5);
u8g2.drawVLine(50, 15, 5);
u8g2.drawVLine(74, 15, 5);
u8g2.drawHLine(60, 10, 5);
u8g2.drawHLine(60, 24, 5);
u8g2.drawVLine(55, 15, 5);
u8g2.drawVLine(69, 15, 5);
u8g2.drawHLine(60, 15, 5);
u8g2.drawHLine(60, 19, 5);
u8g2.drawVLine(60, 15, 5);
u8g2.drawVLine(64, 15, 5);
u8g2.drawStr(0,44,"Height:"); // write something to the internal memory
u8g2.drawStr(0,54,"ShiftX:"); // write something to the internal memory
u8g2.drawStr(0,64,"ShiftY:"); // write something to the internal memory
u8g2.drawStr(68,44,"Yaw:"); // write something to the internal memory
u8g2.drawStr(68,54,"Pitch:"); // write something to the internal memory
u8g2.drawStr(68,64,"Roll:"); // write something to the internal memory
u8g2.drawStr(44,44,pot1); // write something to the internal memory
u8g2.drawStr(44,54,pot2); // write something to the internal memory
u8g2.drawStr(44,64,pot3); // write something to the internal memory
u8g2.drawStr(110,44,pot4); // write something to the internal memory
u8g2.drawStr(110,54,pot5); // write something to the internal memory
u8g2.drawStr(110,64,pot6); // write something to the internal memory
u8g2.sendBuffer(); // transfer internal memory to the display
}
void joystickCalib(){
u8g2.clear(); // clear the display
u8g2.drawStr(0,20,"CALIBRATE JOYSTICK"); // write something to the internal memory
u8g2.drawStr(0,30,"You've got 3 seconds");
u8g2.drawStr(0,40,"for moves. READY?");
u8g2.sendBuffer(); // transfer internal memory to the display
delay(5000);
u8g2.clear();
u8g2.drawStr(0,20,"Center L-joystick");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
LXmid=analogRead(A9);
delay(2);
LYmid=analogRead(A8);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
Serial.println(LXmid);
Serial.println(LYmid);
u8g2.drawStr(0,20,"Push L-joystick UP");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
LYup=analogRead(A8);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
Serial.println(LYup);
u8g2.drawStr(0,20,"Push L-joystick DOWN");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
LYdown=analogRead(A8);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
Serial.println(LYdown);
u8g2.drawStr(0,20,"Push L-joystick LEFT");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
LXleft=analogRead(A9);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
Serial.println(LXleft);
u8g2.drawStr(0,20,"Push L-joystick RIGHT");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
LXright=analogRead(A9);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
Serial.println(LXright);
u8g2.clear();
u8g2.drawStr(0,20,"Center R-joystick");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
RXmid=analogRead(A11);
delay(2);
RYmid=analogRead(A10);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
u8g2.drawStr(0,20,"Push R-joystick UP");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
RYup=analogRead(A10);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
u8g2.drawStr(0,20,"Push R-joystick DOWN");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
RYdown=analogRead(A10);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
u8g2.drawStr(0,20,"Push R-joystick LEFT");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
RXleft=analogRead(A11);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
u8g2.drawStr(0,20,"Push R-joystick RIGHT");
u8g2.sendBuffer();
delay(3000);
u8g2.clear();
RXright=analogRead(A11);
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
//SEND CALIBRATION DATA TO EEPROMWriteInt() FUNCTION FOR WRITING TO EEPROM
EEPROMWriteInt(2, LXmid);
EEPROMWriteInt(5, LYmid);
EEPROMWriteInt(10, LYup);
EEPROMWriteInt(15, LYdown);
EEPROMWriteInt(20, LXleft);
EEPROMWriteInt(25, LXright);
EEPROMWriteInt(30, RXmid);
EEPROMWriteInt(35, RYmid);
EEPROMWriteInt(40, RYup);
EEPROMWriteInt(45, RYdown);
EEPROMWriteInt(50, RXleft);
EEPROMWriteInt(55, RXright);
EEPROMWriteInt(0, 1); //MARK THAT EEPROM HAS BEEN WRITTEN AT LEAST ONCE
setup();//reboot(); //REBOOT THE ARDUINO SO THE VALUES ARE READ FRESH OUT OF THE EEPROM (NOT WORKING YET)
}
void reboot() {
wdt_disable();
wdt_enable(WDTO_15MS);
while (1) {}
}
void setup(void) {
Serial.begin(115200);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(A12, OUTPUT);
u8g2.begin();
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_sirclivethebold_tr); // choose a suitable font
u8g2.drawStr(20,17,"SpotMicro"); // write text to display
u8g2.drawBitmap( 45, 20, 5, 29, spot_bitmap); // write SpotMicro image to display
u8g2.drawStr(18,57,"Remote v1.0");
u8g2.sendBuffer();
delay(5000);
if(EEPROM.read(0) == 255){
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.clear(); // clear the display
u8g2.drawStr(0,20,"Joysticks are not yet"); // write something to the internal memory
u8g2.drawStr(0,30,"calibrated! Push 1st");
u8g2.drawStr(0,40,"button to do so anytime");
u8g2.drawStr(1,57,"|");
u8g2.drawStr(2,57,"|");
u8g2.drawStr(0,64,"V");
u8g2.sendBuffer(); // transfer internal memory to the display;
delay(5000);
}
if(EEPROM.read(0) == 1){
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.clear(); // clear the display
u8g2.drawStr(0,20,"To re-calibrate the"); // write something to the internal memory
u8g2.drawStr(0,30,"joysticks, at anytime");
u8g2.drawStr(0,40,"push the 1st button.");
u8g2.drawStr(1,57,"|");
u8g2.drawStr(2,57,"|");
u8g2.drawStr(0,64,"V");
u8g2.sendBuffer(); // transfer internal memory to the display;
delay(4000);
u8g2.clearBuffer(); // clear the internal memory
u8g2.clear(); // clear the display
u8g2.drawStr(0,20,"To erase the"); // write something to the internal memory
u8g2.drawStr(0,30,"calibration in eeprom,");
u8g2.drawStr(0,40,"push the 2nd button.");
u8g2.drawStr(59,57,"|");
u8g2.drawStr(60,57,"|");
u8g2.drawStr(58,64,"V");
u8g2.sendBuffer();
EEPROM.write(0, 2); // Make sure the calibration messages aren't shown
delay(4000);
} // after the 2nd reboot.
//READ CALIBRATION VALUES FROM EEPROM
LXmid=EEPROMReadInt(2);
LYmid=EEPROMReadInt(5);
LYup=EEPROMReadInt(10);
LYdown=EEPROMReadInt(15);
LXleft=EEPROMReadInt(20);
LXright=EEPROMReadInt(25);
RXmid=EEPROMReadInt(30);
RYmid=EEPROMReadInt(35);
RYup=EEPROMReadInt(40);
RYdown=EEPROMReadInt(45);
RXleft=EEPROMReadInt(50);
RXright=EEPROMReadInt(55);
Serial.println(LXmid);
Serial.println(LYmid);
Serial.println(LYup);
Serial.println(LYdown);
Serial.println(LXleft);
Serial.println(LXright);
radio.begin(); //Starting the Wireless communication
radio.openWritingPipe(address); //Setting the address where we will send the data
radio.setPALevel(RF24_PA_MIN); //You can set it as minimum or maximum depending on the distance between the transmitter and receiver.
radio.stopListening(); //This sets the module as transmitter
}
void loop() {
//FIRST BUTTON BETWEEN JOYSTICKS ACTIVATES JOYSTICK CALIBRATION
if(digitalRead(5)== LOW){ //button pressed (JOYSTICK CALIBRATION)
joystickCalib();
}
if(digitalRead(4)== LOW){ //button pressed (ERASE EEPROM)
for(int i=0;i<100;i++){
EEPROM.write(i, 255);
}
}
if(digitalRead(3)== LOW){ //CHANGE WALKING GAIT)
if(gait<2){ //SET TO A MAX OF 3 DIFFERENT WALKING GAITS
gait++; //GO TO THE NEXT GAIT (VALUE IS SENT TO THE ROBOT)
}else{
gait = 0; //IF GAIT IS HIGHER THAN 2 (3RD GAIT), RESET TO FIRST GAIT
}
delay(300);
}
//READ TOGGLE SWITCHES
int toggle1 = digitalRead(7); //HIGH=Autonomous LOW=Manual control
int toggle2 = digitalRead(6); //HIGH=Servo-power-on LOW=Servo-power-off
//READ KNOB VALUES 3 TIMES AND CALCULATE AVERAGE
int temp11=analogRead(A0);
int temp12=analogRead(A0);
int temp13=analogRead(A0);
int temp1=(temp11+temp12+temp13)/3;
int temp21=analogRead(A1);
int temp22=analogRead(A1);
int temp23=analogRead(A1);
int temp2=(temp21+temp22+temp23)/3;
int temp31=analogRead(A2);
int temp32=analogRead(A2);
int temp33=analogRead(A2);
int temp3=(temp31+temp32+temp33)/3;
int temp41=analogRead(A3);
int temp42=analogRead(A3);
int temp43=analogRead(A3);
int temp4=(temp41+temp42+temp43)/3;
int temp51=analogRead(A4);
int temp52=analogRead(A4);
int temp53=analogRead(A4);
int temp5=(temp51+temp52+temp53)/3;
int temp61=analogRead(A5);
int temp62=analogRead(A5);
int temp63=analogRead(A5);
int temp6=(temp61+temp62+temp63)/3;
//CONVERSION FOR WRITING KNOB VALUES TO DISPLAY
sprintf(pot1, "%d", temp1); //height
sprintf(pot2, "%d", temp2); //shiftX
sprintf(pot3, "%d", temp3); //shiftY
sprintf(pot4, "%d", temp4); //roll
sprintf(pot5, "%d", temp5); //pitch
sprintf(pot6, "%d", temp6); //yaw
sprintf(gaitVersion, "%d", gait+1);
//READ JOYSTICK VALUES 3 TIMES AND TAKE THE AVERAGE
int potA=(analogRead(A8));
int potA1=(analogRead(A8));
int potA2=(analogRead(A8));
potA=(potA+potA1+potA2)/3;
int potB=(analogRead(A9));
int potB1=(analogRead(A9));
int potB2=(analogRead(A9));
potB=(potB+potB1+potB2)/3;
int potC=(analogRead(A10));
int potC1=(analogRead(A10));
int potC2=(analogRead(A10));
potC=(potC+potC1+potC2)/3;
int potD=(analogRead(A11));
int potD1=(analogRead(A11));
int potD2=(analogRead(A11));
potD=(potD+potD1+potD2)/3;
//CONVERT JOYSTICK VALUES TO VALUES BETWEEN 0 AND 2000, 1000 BEING THE CENTRE
if(potA > LYmid){
LY = map(potA, LYmid, LYdown, 1000, 2000);
}
else {
LY = map(potA, LYup, LYmid, 0, 1000);
}
if(potB > LXmid){
LX = map(potB, LXmid, LXleft, 1000, 2000);
}
else {
LX = map(potB, LXright, LXmid, 0, 1000);
}
if(potC > RYmid){
RY = map(potC, RYmid, RYdown, 1000, 2000);
}
else {
RY = map(potC, RYup, RYmid, 0, 1000);
}
if(potD > RXmid){
RX = map(potD, RXmid, RXleft, 1000, 2000);
}
else {
RX = map(potD, RXright, RXmid, 0, 1000);
}
//PUTTING ALL VALUES IN AN ARRAY FOR SENDING IT OVER NRF24L01 RADIO
Array[0]=temp1;
Array[1]=temp2;
Array[2]=temp3;
Array[3]=temp4;
Array[4]=temp5;
Array[5]=temp6;
Array[6]=2000-LY; //INVERTED VALUES (DOWN IS 0 AND UP IS 2000)
Array[7]=2000-LX; //INVERTED VALUES (LEFT IS 0 AND RIGHT IS 2000)
Array[8]=2000-RY;; //INVERTED VALUES (DOWN IS 0 AND UP IS 2000)
Array[9]=2000-RX; //INVERTED VALUES (LEFT IS 0 AND RIGHT IS 2000)
Array[10]=toggle1;
Array[11]=toggle2;
Array[12]=gait;
radio.write( &Array, sizeof(Array));
digitalWrite(A12, HIGH); //LED ON
delay(1);
digitalWrite(A12, LOW); //LED OFF
for(int i=0;i<13;i++){
Serial.print(Array[i]);
Serial.print(",");
}
Serial.print("\n");
writeDisplay();
//delay(20);
}