Related
In my program on Arduino I use a TimerInterrupt. In this TimerIntrrupt I try to display on a LCD the values returned by the methods I call. Unfortunately nothing is displayed on the LCD and I don't know where the error is in my program. Does anyone have an idea of the error? How can I handle to solve the problem
class Sensor {
float _flowRateTrinkwasser;
unsigned long _wasserMengeTrinkwasser;
float _flowRateReinwasser;
unsigned long _wasserMengeReinwasser;
public:
Sensor( float flowRateTrinkwasser, unsigned long wasserMengeTrinkwasser, float flowRateReinwasser, unsigned long wasserMengeReinwasser) { // Konstruktor
_flowRateTrinkwasser = flowRateTrinkwasser;
_wasserMengeTrinkwasser = wasserMengeTrinkwasser;
_flowRateReinwasser = flowRateReinwasser;
_wasserMengeReinwasser = wasserMengeReinwasser;
}
int get_f_ml1 () {
_flowRateTrinkwasser = 120; // Formel zur Berechnung von Flow Rate
_wasserMengeTrinkwasser = 50;
int f_ml1 = _flowRateTrinkwasser + _wasserMengeTrinkwasser ;
return f_ml1;
}
int get_f_ml () {
_flowRateReinwasser = 70;
_wasserMengeReinwasser = 40;
int f_ml = _flowRateReinwasser + _wasserMengeReinwasser ;
return f_ml;
}
};
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "test.h"
//Durchflussmessung Variable Trinkwasser//
float flowRateTrinkwasser;
unsigned long wasserMengeTrinkwasser;
//Durchflussmessung Variable Trinkwasser//
//Durchflussmessung Variable Reinwasser//
float flowRateReinwasser;
unsigned long wasserMengeReinwasser;
//Durchflussmessung Variable Reinwasser//
Sensor sensorOne( flowRateTrinkwasser, wasserMengeTrinkwasser, flowRateReinwasser, wasserMengeReinwasser ); // Objekt vom Konstruktor Sensor1
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup() {
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.println("Willkommen bei Truu");
delay(2000);
lcd.clear();
cli(); // disable interrupts
// reset
TCCR1A = 0; //set TCCR1A register to 0000
TCCR1B = 0; //set TCCR1B register to 0
TCNT1 = 0; // reset counter value
OCR1A = 15624; //compare match register für 1 Sekunde (15624pulse vorher)
// set prescaler
TCCR1B |= (1 << CS12) | (1 << CS10);
TCCR1B |= (1 << WGM12); //turn on CTC mode
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei(); //allow interrupts
}
void loop() {
}
ISR(TIMER1_COMPA_vect) { // function which will be called when an interrupt occure at timer 1
lcd.setCursor(0,0);
lcd.println(sensorOne.get_f_ml1());
lcd.setCursor(0,1);
lcd.println(sensorOne.get_f_ml());
delay(50);
}
I have a very simple question, I suppose... I am programming on a SAMD21 board with two SPI communications (one classis SPI and second int SPI), In my opinion the int SPI does not work correctly. If i connect some senzor, it does print nothing to the serial monitor. I am quite sure, that my code is OK, but perhaps there can be some mistake...
So I need to know, if there is some way to test SPI winhout connecting external devices to the board. Please, do not hate that the code is too long, the code for SPI sensor I am using is marked with "//SPI".
PS: Sorry I deleted the SMUART and MICS6814 measuring and calibrating functions, to use only 30 000 characters. Full code is here: https://mega.nz/file/2QxG0ayZ#4UpEU17ogXnm2NK22zAowlDDRrXzksPzzs05zbuq23o
#include <Adafruit_BME280.h> // include Adafruit BME280 library
#include <Adafruit_INA219.h> // include INA219
#include <SD.h> // include Arduino SD library
#include "Open_Cansat_GPS.h"
#include <RTCZero.h>
//include our new sensors
#include <MICS6814.h>
#include <MICS-VZ-89TE.h>
#include "MQ131.h"
#include <Wire.h>
#include <SPI.h>
#include "RFM69.h" // include RFM69 library
// Local
#define PC_BAUDRATE 115200
#define MS_DELAY 0 // Number of milliseconds between data sending and LED signalization
#define LED_DELAY 100
#define Serial SerialUSB
RTCZero rtc;
// RFM69
#define NETWORKID 0 // Must be the same for all nodes (0 to 255)
#define MYNODEID 1 // My node ID (0 to 255)
#define TONODEID 2 // Destination node ID (0 to 254, 255 = broadcast)
#define FREQUENCY RF69_433MHZ // Frequency set up
#define FREQUENCYSPECIFIC 433000000 // Should be value in Hz, now 433 Mhz will be set
#define CHIP_SELECT_PIN 43 //radio chip select
#define INTERUP_PIN 9 //radio interrupt
// BME280 SETTING
#define BME280_ADDRESS_OPEN_CANSAT 0x77
#define SEALEVELPRESSURE_HPA 1013.25
//define our sensor pins
//MICS6814
#define PIN_CO 0
#define PIN_NO2 0
#define PIN_NH3 0
#define MICS6814Pin 6
//OZONE2CLICK
const byte pinSS = 2;
const byte pinRDY = 12;
const byte pinSCK = 13;
const byte O2Pin = 10;
//SMUART
bool flag1 = false;
bool flag2 = false;
byte rxData[30];
byte one, two;
#define DcPin 8
//MICSVZ89TE
#define MICSVZ89TEPin 7
int datas [7];
bool MICS_status;
int MICS_voc;
int MICS_eqco2;
//PHASES
float CurrentTime;
const byte day = 17;
const byte month = 11;
const byte year = 15;
MICS6814 gas(PIN_CO, PIN_NO2, PIN_NH3);
long accelX, accelY, accelZ;
float gForceX, gForceY, gForceZ;
long gyroX, gyroY, gyroZ;
float rotX, rotY, rotZ;
#define sd_cs_pin 35 // set SD's chip select pin (according to the circuit)
RFM69 radio(CHIP_SELECT_PIN, INTERUP_PIN, true);
typedef struct
{
int16_t messageId;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t sec;
float longitude;
float latitude;
uint8_t num_of_satelites;
float temperature;
float pressure;
float altitude;
float humidity_bme280;
float voltage_shunt;
float voltage_bus;
float current_mA;
float voltage_load;
int16_t rssi;
} messageOut;
messageOut cansatdata; //create the struct variable
Adafruit_BME280 bme;
Adafruit_INA219 ina219(0x40);
OpenCansatGPS gps;
File file; // SD library variable
// LEDS
#define D13_led_pin 42 // D13 LED
#define M_led_pin 36 // MLED
// Local variables
int idCounter = 1;
bool isBmeOk = true;
bool isSdOk = true;
bool isRadioOk = true;
bool isGpsConnected = true;
// My variables
float NH3Data;
float COData;
float NO2Data;
float PPMO2;
float PPBO2;
float MGM3O2;
float UGM3O2;
float SSmoke1;
float SSmoke2;
float SSmoke3;
float ESmoke1;
float ESmoke2;
float ESmoke3;
int DataCounter = 0;
void RTCBegin ()
{
rtc.begin();
}
void RTCSleep ()
{
digitalWrite(O2Pin, LOW);
digitalWrite(MICS6814Pin, LOW);
digitalWrite(MICSVZ89TEPin, LOW);
digitalWrite(DcPin, LOW);
digitalWrite(D13_led_pin, LOW);
digitalWrite(M_led_pin, LOW);
GyroscopeSleep();
byte seconds = 0;
byte minutes = 00;
byte hours = 00;
rtc.setTime(hours, minutes, seconds);
rtc.setDate(day, month, year);
rtc.setAlarmTime(00, 00, 10);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.standbyMode();
TerrestrialPhase();
// Sleep until next alarm match
}
//SPI
void OZONE2CLICKCalibrate ()
{
Serial.println("2");
//MQ131.begin(pinSS, pinRDY, O2Pin, LOW_CONCENTRATION, 10000); //(int _pinCS, int _pinRDY, int _pinPower, MQ131Model _model, int _RL)
Serial.println("99");
Serial.println("Calibration in progress...");
MQ131.calibrate();
Serial.println("Calibration done!");
Serial.print("R0 = ");
Serial.print(MQ131.getR0());
Serial.println(" Ohms");
Serial.print("Time to heat = ");
Serial.print(MQ131.getTimeToRead());
Serial.println(" s");
}
void OZONE2CLICKMeasure ()
{
Serial.println("Sampling...");
MQ131.sample();
Serial.print("Concentration O3 : ");
PPMO2 = MQ131.getO3(PPM);
Serial.print(PPMO2);
Serial.println(" ppm");
Serial.print("Concentration O3 : ");
PPBO2 = MQ131.getO3(PPB);
Serial.print(PPBO2);
Serial.println(" ppb");
Serial.print("Concentration O3 : ");
MGM3O2 = MQ131.getO3(MG_M3);
Serial.print(MGM3O2);
Serial.println(" mg/m3");
Serial.print("Concentration O3 : ");
UGM3O2 = MQ131.getO3(UG_M3);
Serial.print(UGM3O2);
Serial.println(" ug/m3");
}
//SPI
void MICSVZ89TEMeasure()
{
delay(5000);
MICS_VZ_89TE myMICS;
MICS_status = myMICS.begin();
myMICS.readSensor();
MICS_eqco2 = myMICS.getCO2();
MICS_voc = myMICS.getVOC();
Serial.print("MICS VOC PPB: ");
Serial.println(MICS_voc);
Serial.print("MICS CO2 PPM: ");
Serial.println(MICS_eqco2);
delay(1000);
}
void GyroscopeTurnOn()
{
Wire.begin();
setupMPU();
}
void GyroscopeMeasure() {
recordAccelRegisters();
recordGyroRegisters();
printData();
}
void setupMPU(){
Wire.beginTransmission(0b1101000); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2)
Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28)
Wire.write(0b00000000); //Setting SLEEP register to 0. (Required; see Note on p. 9)
Wire.endTransmission();
Wire.beginTransmission(0b1101000); //I2C address of the MPU
Wire.write(0x1B); //Accessing the register 1B - Gyroscope Configuration (Sec. 4.4)
Wire.write(0x00000000); //Setting the gyro to full scale +/- 250deg./s
Wire.endTransmission();
Wire.beginTransmission(0b1101000); //I2C address of the MPU
Wire.write(0x1C); //Accessing the register 1C - Acccelerometer Configuration (Sec. 4.5)
Wire.write(0b00000000); //Setting the accel to +/- 2g
Wire.endTransmission();
}
void GyroscopeSleep(){
Wire.beginTransmission(0b1101000); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2)
Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28)
Wire.write(0b01000000); //Setting SLEEP register to 0. (Required; see Note on p. 9)
Wire.endTransmission();
}
void recordAccelRegisters() {
Wire.beginTransmission(0b1101000); //I2C address of the MPU
Wire.write(0x3B); //Starting register for Accel Readings
Wire.endTransmission();
Wire.requestFrom(0b1101000,6); //Request Accel Registers (3B - 40)
while(Wire.available() < 6);
accelX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX
accelY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
accelZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
processAccelData();
}
void processAccelData(){
gForceX = accelX / 16384.0;
gForceY = accelY / 16384.0;
gForceZ = accelZ / 16384.0;
}
void recordGyroRegisters() {
Wire.beginTransmission(0b1101000); //I2C address of the MPU
Wire.write(0x43); //Starting register for Gyro Readings
Wire.endTransmission();
Wire.requestFrom(0b1101000,6); //Request Gyro Registers (43 - 48)
while(Wire.available() < 6);
gyroX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX
gyroY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
gyroZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
processGyroData();
}
void processGyroData() {
rotX = gyroX / 131.0;
rotY = gyroY / 131.0;
rotZ = gyroZ / 131.0;
}
void printData() {
Serial.print("Gyro (deg)");
Serial.print(" X=");
Serial.print(rotX);
Serial.print(" Y=");
Serial.print(rotY);
Serial.print(" Z=");
Serial.print(rotZ);
Serial.print(" Accel (g)");
Serial.print(" X=");
Serial.print(gForceX);
Serial.print(" Y=");
Serial.print(gForceY);
Serial.print(" Z=");
Serial.println(gForceZ);
}
void TerrestrialPhase()
{
Serial.println("------------------------------");
Serial.println("TERRESTRIAL PHASE IN PROGRESS");
Serial.println("------------------------------");
Serial.println(" ");
digitalWrite(DcPin, HIGH);
digitalWrite(MICS6814Pin, HIGH);
digitalWrite(MICSVZ89TEPin, HIGH);
digitalWrite(O2Pin, HIGH);
delay(2000);
MICSVZ89TEMeasure();
MICSVZ89TEMeasure();
MICSVZ89TEMeasure();
MICSVZ89TEMeasure();
MICS6814Calibrate();
MICS6814Measure();
SMUARTTurnOn();
delay(10000);
SMUARTMeasure();
delay(10000);
SMUARTMeasure();
delay(1000);
SMUARTMeasure();
delay(1000);
SMUARTMeasure();
delay(1000);
OZONE2CLICKCalibrate();
OZONE2CLICKMeasure();
SDSave();
delay(10000);
RTCBegin();
RTCSleep();
}
void LandingChecker()
{
CurrentTime = millis();
Serial.println(CurrentTime);
if (CurrentTime >= 30000)
{
Serial.println("------------------------------");
Serial.println("AIR PHASE DONE");
Serial.println("------------------------------");
Serial.println(" ");
Serial.println("------------------------------");
Serial.println("STARTING TESTING PHASE");
Serial.println("------------------------------");
Serial.println(" ");
TestingPhase1();
}
else
{
Serial.println("------------------------------");
Serial.println("AIR PHASE IN PROGRESS");
Serial.println("------------------------------");
Serial.println(" ");
}
}
void WaitingPhase()
{
digitalWrite(O2Pin, LOW);
digitalWrite(MICS6814Pin, LOW);
digitalWrite(MICSVZ89TEPin, LOW);
digitalWrite(DcPin, LOW);
digitalWrite(D13_led_pin, LOW);
digitalWrite(M_led_pin, LOW);
GyroscopeSleep();
byte seconds = 0;
byte minutes = 00;
byte hours = 00;
rtc.setTime(hours, minutes, seconds);
rtc.setDate(day, month, year);
rtc.setAlarmTime(00, 00, 10);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.standbyMode();
TerrestrialPhase();
}
void TestingPhase2()
{
Serial.println("------------------------------");
Serial.println("TESTING PHASE DONE");
Serial.println("------------------------------");
Serial.println(" ");
Serial.println("------------------------------");
Serial.println("STARTING TERRESTRIAL PHASE");
Serial.println("------------------------------");
Serial.println(" ");
TerrestrialPhase();
}
void TestingPhase1()
{
if (((gForceX >= 3.60) || (gForceX <= 0.40)) && ((gForceY >= 2.9) && (gForceY <= 3.20)))
{
Serial.println("------------------------------");
Serial.println("SATELITE POSITION: VERY GOOD");
Serial.println("------------------------------");
Serial.println(" ");
TestingPhase2();
}
else if (((gForceX >= 3.30) || (gForceX <= 0.70)) && ((gForceY >= 2.9) && (gForceY <= 3.30)))
{
Serial.println("------------------------------");
Serial.println("SATELITE POSITION: GOOD");
Serial.println("------------------------------");
Serial.println(" ");
TestingPhase2();
}
else if (((gForceX >= 3) || (gForceX <= 1)) && ((gForceY >= 2.9) && (gForceY <= 4)))
{
Serial.println("------------------------------");
Serial.println("SATELITE POSITION: OK");
Serial.println("------------------------------");
Serial.println(" ");
TestingPhase2();
}
else
{
Serial.println("------------------------------");
Serial.println("SATELITE POSITION: BAD");
Serial.println("------------------------------");
Serial.println(" ");
Serial.println("------------------------------");
Serial.println("TESTING PHASE FAILED");
Serial.println("------------------------------");
Serial.println(" ");
WaitingPhase();
}
}
void SDSave()
{
while(!Serial);
if (!SD.begin(sd_cs_pin))
{
Serial.println("SD card initialization failed!");
return;
}
Serial.println("SD card initialized");
file = SD.open("data.txt", FILE_WRITE); // Open test.txt for write
// Rrite to the file and print info to serial
// print an error to the serial in case it does not succeed
if (file)
{
file.println("--------------------------");
file.println("GyKoVySAT terrestrial data");
file.println("--------------------------");
file.println("OZONE");
file.println(PPMO2);
file.print(" ppm");
file.println(PPBO2);
file.print(" ppb");
file.println(MGM3O2);
file.print(" mg/m³");
file.println("SMOKE PARTICLES STANDART");
file.println(UGM3O2);
file.print(" μg/m³");
file.println(SSmoke1);
file.print(" μg/m³");
file.println(SSmoke2);
file.print(" μg/m³");
file.println(SSmoke3);
file.println("SMOKE PARTICLES ENVIROMENTAL");
file.println(ESmoke1);
file.print(" μg/m³");
file.println(ESmoke2);
file.print(" μg/m³");
file.println(ESmoke3);
file.print(" μg/m³");
file.println("VOC");
file.println(MICS_voc);
file.print(" ppm");
file.println("CO2");
file.println(MICS_eqco2);
file.print(" ppm");
file.println("CO");
file.println(COData);
file.print(" ppm");
file.println("NO2");
file.println(NO2Data);
file.print(" ppm");
file.println("NH3");
file.println(NH3Data);
file.print(" ppm");
file.println("DATA FOR ANALYZATION");
file.println(PPMO2);
file.print(";");
file.print(PPBO2);
file.print(";");
file.print(MGM3O2);
file.print(";");
file.print(UGM3O2);
file.print(";");
file.print(SSmoke1);
file.print(";");
file.print(SSmoke2);
file.print(";");
file.print(SSmoke3);
file.print(";");
file.print(ESmoke1);
file.print(";");
file.print(ESmoke2);
file.print(";");
file.print(ESmoke3);
file.print(";");
file.print(MICS_voc);
file.print(";");
file.print(MICS_eqco2);
file.print(";");
file.print(COData);
file.print(";");
file.print(NO2Data);
file.print(";");
file.print(NH3Data);
file.print(";");
file.print(DataCounter);
file.print(";");
file.close();
DataCounter = DataCounter + 1;
}
else
{
Serial.println("Error opening data.txt for writing");
}
TerrestrialPhase();
}
void setup()
{
// wait for the Arduino serial (on your PC) to connect
// please, open the Arduino serial console (right top corner)
// note that the port may change after uploading the sketch
// COMMENT OUT FOR USAGE WITHOUT A PC!
// while(!Serial);
Serial.println("openCanSat PRO");
Serial.print("Node ");
Serial.print(MYNODEID,DEC);
Serial.println(" ready");
// begin communication with the BME280 on the previously specified address
// print an error to the serial in case the sensor is not found
if (!bme.begin(BME280_ADDRESS_OPEN_CANSAT))
{
isBmeOk = false;
Serial.println("Could not find a valid BME280 sensor, check wiring!");
return;
}
// begin communication with the INA219
ina219.begin();
// check of Gps is connected
Wire.beginTransmission(0x42); // 42 is addres of GPS
int error = Wire.endTransmission();
if (error != 0)
{
isGpsConnected = false;
}
// begin communication with gps
gps.begin();
// Uncomment when you want to see debug prints from GPS library
// gps.debugPrintOn(57600);
if(!radio.initialize(FREQUENCY, MYNODEID, NETWORKID))
{
isRadioOk = false;
Serial.println("RFM69HW initialization failed!");
}
else
{
radio.setFrequency(FREQUENCYSPECIFIC);
radio.setHighPower(true); // Always use this for RFM69HW
}
pinMode(D13_led_pin, OUTPUT);
pinMode(DcPin, OUTPUT);
pinMode(MICS6814Pin, OUTPUT);
pinMode(MICSVZ89TEPin, OUTPUT);
pinMode(O2Pin, OUTPUT);
GyroscopeTurnOn();
}
void loop()
{
cansatdata.messageId = idCounter;
GyroscopeMeasure();
LandingChecker();
Serial.println("MessageId = " + static_cast<String>(cansatdata.messageId));
cansatdata.temperature = 0;
cansatdata.pressure = 0;
cansatdata.altitude = 0;
if(isBmeOk)
{
cansatdata.temperature += bme.readTemperature();
cansatdata.pressure += bme.readPressure() / 100.0F;
cansatdata.altitude += bme.readAltitude(SEALEVELPRESSURE_HPA);
cansatdata.humidity_bme280 = bme.readHumidity();
}
Serial.println("Temperature = " + static_cast<String>(cansatdata.temperature) + " *C");
Serial.println("Pressure = " + static_cast<String>(cansatdata.pressure) + " Pa");
Serial.println("Approx altitude = " + static_cast<String>(cansatdata.altitude) + " m");
Serial.println("Humidity = " + static_cast<String>(cansatdata.humidity_bme280) + " %");
// read values from INA219 into structure
cansatdata.voltage_shunt = ina219.getShuntVoltage_mV();
cansatdata.voltage_bus = ina219.getBusVoltage_V();
cansatdata.current_mA = ina219.getCurrent_mA();
cansatdata.voltage_load = cansatdata.voltage_bus + (cansatdata.voltage_shunt / 1000);
Serial.println("Shunt Voltage: " + static_cast<String>(cansatdata.voltage_shunt) + " mV");
Serial.println("Bus Voltage: " + static_cast<String>(cansatdata.voltage_bus) + " V");
Serial.println("Current: " + static_cast<String>(cansatdata.current_mA) + " mA");
Serial.println("Load Voltage: " + static_cast<String>(cansatdata.voltage_load) + " V");
// Initialize GPS
cansatdata.year = 0;
cansatdata.month = 0 ;
cansatdata.day = 0;
cansatdata.hour = 0;
cansatdata.minute = 0;
cansatdata.sec = 0;
cansatdata.latitude = 0;
cansatdata.longitude = 0;
cansatdata.num_of_satelites = 0;
// save start time in millisec
uint32_t start = millis();
// END LED BLINK
digitalWrite(D13_led_pin, LOW);
pinMode(M_led_pin, INPUT);
// END LED BLINK
if(isGpsConnected)
{
if (gps.scan(250))
{
cansatdata.year = gps.getYear();
cansatdata.month = gps.getMonth();
cansatdata.day = gps.getDay();
cansatdata.hour = gps.getHour();
cansatdata.minute = gps.getMinute();
cansatdata.sec = gps.getSecond();
cansatdata.latitude = gps.getLat();
cansatdata.longitude = gps.getLon();
cansatdata.num_of_satelites = gps.getNumberOfSatellites();
Serial.println(String("Time to find fix: ") + (millis() - start) + String("ms"));
Serial.println(String("Datetime: ") + String(cansatdata.year) + "/"+ String(cansatdata.month) + "/"+ String(cansatdata.day) + " " + String(cansatdata.hour) + ":"+ String(cansatdata.minute) + ":"+ String(cansatdata.sec));
Serial.println(String("Lat: ") + String(cansatdata.latitude, 7));
Serial.println(String("Lon: ") + String(cansatdata.longitude, 7));
Serial.println(String("Num of sats: ") + String(cansatdata.num_of_satelites));
Serial.println();
}
else
{
Serial.println("Gps have no satelit to fix.");
}
}
// RFM69HW
cansatdata.rssi = 0;
if(isRadioOk)
{
cansatdata.rssi = radio.RSSI;
Serial.println("Signal = " + static_cast<String>(radio.RSSI));
radio.send(TONODEID, (const void*)&cansatdata, sizeof(cansatdata));
}
Serial.println();
// START LED hart beat
pinMode(M_led_pin, OUTPUT);
digitalWrite(D13_led_pin, HIGH);
digitalWrite(M_led_pin, HIGH);
// START LED hart beat
if(!isGpsConnected)
{
delay(200);
}
idCounter ++;
}
most SPI interfaces can be placed in a 'loopback' mode, which makes it easy for the code to check the activity to verify the transmitted/received data
regarding:
if (error != 0)
{
isGpsConnected = false;
}
// begin communication with gps
gps.begin();
should not be trying to communicate with the GPS if no GPS connected. suggest:
if (error != 0)
{
isGpsConnected = false;
return;
}
else
{
// begin communication with gps
gps.begin();
}
I'm trying to execute following source on my RC transmitter and receiver which uses NRF24 module. Today i uploaded same sketch as i did half year ago and it stopped receiving any data from transmitter, nothing is printed in DEBUG_PRINT sections.
On transmitter NRF24 is connected to 7, 8.
On receiver NRF24 is connected to 9, 10.
Wiring checked 7 times, correct, power supply of the receiver is 7.4V to the VIN, power supply for the transmitter is USB source.
Running on the arduino nano.
Source for transmitter is:
// Flash for self-diy NRF24lo* transmiter controller.
// 6 buttons, 4-way sticks, based on CX-10c controller.
#include <EEPROM.h>
#include <SPI.h>
#include "RF24.h"
#define DEBUG_PRINT
// #define DEBUG_PRINT_RAW
#ifdef DEBUG_PRINT
#include <printf.h>
#endif
// IO mappings
#define STICK0 A3
#define STICK1 A2
#define STICK2 A0
#define STICK3 A1
#define BTN0 3
#define BTN1 6
#define BTN2 5
#define BTN3 4
#define BTN4 9
#define BTN5 A4
#define STLED 2
// Optional moddings
#define BTN_LONG_PRESS 1000
#define LED_ST_OFF 0
#define LED_ST_CONST 1
#define LED_ST_FLASH 2
#define LED_ST_FAST_FLASH 3
#define LED_ST_FLASH_TIME 250
#define LED_ST_FAST_FLASH_TIME 100
#define LED_POWER 150
// Package types
#define PACKAGE_STICKS 3
#define PACKAGE_BUTTON 5
#define PACKAGE_PING 7
#define PACKAGE_TIMEOUT 100
// Amount of packages should be dropped sequently to detect disconnect
#define TX_DROP_EDGE 16
// Long press stick 0 to start calibration, long press to stop
// Struct with min/max calibration values
struct STICK_CALIBRATION {
int stmx[4];
int stmn[4];
};
// Struct with info of button presses
struct BTN_STATE {
int press[6];
unsigned long time[6];
// Used in long press to avoid multiple pressing
int acted[6];
};
// Struct with state of flashing/idle/working LED
// FLASH --> CONST/OFF
// FAST_FLASH --> CONST/OFF
struct LED_STATE {
// Previous type (for FLASH return to OFF/CONST state)
int ptype;
// Type of operation
int type;
// ON/OFF
int state;
// Time since action
unsigned long time;
// Count of flashes
int count;
};
// Sender package type.
// Contains type and values for button or values for sticks
struct Package {
int type;
union {
struct {
int number;
int lpress;
int dummy[2];
} button;
struct {
int sticks[4];
} sticks;
} data;
};
// Info of sticks calibration
STICK_CALIBRATION calibration;
bool calibration_mode = 0;
int sticks[4];
// When entering lock mode, stick data is not updating from input
bool lock_mode = 0;
// info of buttons states
BTN_STATE buttons;
LED_STATE led_state;
// Use NRF24lo1 transmitter on pins 7, 8
RF24 radio(7, 8);
byte addresses[][6] = { "TRAAA", "REEEE" };
// Amount of sequently dropped packages. Used to detect disconnect
int tx_dropped = 0;
// Used to prevent longpress repeat
// On long press button could be used as sticky press function,
// to avoid button sticking and long press act once during while push down,
// use this function to mark button as acted
void btn_act(int nubmer);
// Called on button press/longpress
void btn_action(int number, int lpress);
// Set led ON/OFF with change of state flag
void led_set_state(int state);
// Set led action type (FLAST, CONST, e.t.c.)
void led_set(int type, int count);
// Sends single package with given amount of attempts.
// Allows waiting for responce of receiver. The response should be 0.
// Returns 1 on success, 0 on failture.
int send_package(byte* pack, int size);
void setup() {
#ifdef DEBUG_PRINT
Serial.begin(115200);
#endif
// Set up transmitter
radio.begin();
radio.setChannel(77);
radio.enableAckPayload();
radio.setPALevel(RF24_PA_MAX);
radio.openWritingPipe(addresses[1]);
radio.openReadingPipe(1, addresses[0]);
#ifdef DEBUG_PRINT
printf_begin();
radio.printDetails();
#endif
// Read calibration values
byte* cal_struct = (byte*) &calibration;
for (int i = 0; i < sizeof(STICK_CALIBRATION); ++i)
cal_struct[i] = EEPROM.read(i);
#ifdef DEBUG_PRINT
Serial.println("CALIBRATION: ");
for (int i = 0; i < 4; ++i) {
Serial.print('(');
Serial.print(calibration.stmn[i]);
Serial.print(", ");
Serial.print(calibration.stmx[i]);
Serial.print(") ");
}
Serial.println();
#endif
buttons = {{0,0,0,0,0,0}, {0,0,0,0,0,0}, {0,0,0,0,0,0}};
// Clear LED state
led_state.ptype = 0;
led_state.type = 0;
led_state.state = 0;
led_state.time = 0;
led_state.count = 0;
// Prepare pinout
pinMode(STLED, OUTPUT);
pinMode(STICK0, INPUT_PULLUP);
pinMode(STICK1, INPUT_PULLUP);
pinMode(STICK2, INPUT_PULLUP);
pinMode(STICK3, INPUT_PULLUP);
pinMode(BTN0, INPUT_PULLUP);
pinMode(BTN1, INPUT_PULLUP);
pinMode(BTN2, INPUT_PULLUP);
pinMode(BTN3, INPUT_PULLUP);
pinMode(BTN4, INPUT_PULLUP);
pinMode(BTN5, INPUT_PULLUP);
}
void loop() {
// Read sticks & map to calibration
if (!lock_mode) {
sticks[0] = analogRead(STICK0);
sticks[1] = analogRead(STICK1);
sticks[2] = 1024 - analogRead(STICK2);
sticks[3] = 1024 - analogRead(STICK3);
}
// Read stick buttons
int rbtn[6];
rbtn[0] = !digitalRead(BTN0);
rbtn[1] = !digitalRead(BTN1);
// read optional buttons
rbtn[2] = !digitalRead(BTN2);
rbtn[3] = !digitalRead(BTN3);
rbtn[4] = !digitalRead(BTN4);
rbtn[5] = !digitalRead(BTN5);
#ifdef DEBUG_PRINT
#ifdef DEBUG_PRINT_RAW
// Debug out
Serial.print(sticks[0]); Serial.print(' ');
Serial.print(sticks[1]); Serial.print(' ');
Serial.print(sticks[2]); Serial.print(' ');
Serial.print(sticks[3]); Serial.print(' ');
Serial.print(rbtn[0]); Serial.print(' ');
Serial.print(rbtn[1]); Serial.print(' ');
Serial.print(rbtn[2]); Serial.print(' ');
Serial.print(rbtn[3]); Serial.print(' ');
Serial.print(rbtn[4]); Serial.print(' ');
Serial.print(rbtn[5]); Serial.print(' ');
Serial.println();
#endif
#endif
// Map to calibration
if (!calibration_mode && !lock_mode) {
sticks[0] = map(sticks[0], calibration.stmn[0], calibration.stmx[0], 0, 1023);
sticks[1] = map(sticks[1], calibration.stmn[1], calibration.stmx[1], 0, 1023);
sticks[2] = map(sticks[2], calibration.stmn[2], calibration.stmx[2], 0, 1023);
sticks[3] = map(sticks[3], calibration.stmn[3], calibration.stmx[3], 0, 1023);
}
// Check buttons states and update timings
for (int i = 0; i < 6; ++i) {
if (buttons.press[i] && !rbtn[i]) { // Button released
if (!buttons.acted[i])
btn_action(i, (millis() - buttons.time[i]) > BTN_LONG_PRESS);
buttons.press[i] = 0;
buttons.time[i] = 0;
} else if (buttons.press[i]) { // Button keeps down
if ((millis() - buttons.time[i]) > BTN_LONG_PRESS && !buttons.acted[i]) { // Toggle long press
btn_action(i, 1);
// buttons.press[i] = 0;
buttons.time[i] = 0;
}
} else if (rbtn[i]) { // Button pressed
buttons.press[i] = 1;
buttons.acted[i] = 0;
buttons.time[i] = millis();
}
}
// Update LED
if (led_state.type == LED_ST_FLASH && (led_state.time + LED_ST_FLASH_TIME) < millis()
||
led_state.type == LED_ST_FAST_FLASH && (led_state.time + LED_ST_FAST_FLASH_TIME) < millis()) { // Flash period done
if (!led_state.state) { // Count cycle as finished, try to begin another one
--led_state.count;
if (led_state.count <= 0) { // Flashing cycles is done
led_state.type = led_state.ptype;
led_set_state(led_state.type == LED_ST_CONST);
} else { // Turn led ON again, begin next flash cycle
led_state.time = millis();
led_set_state(1);
}
} else { // Just turn the led OFF
led_state.time = millis();
led_set_state(0);
}
}
// Update led flashing
if (led_state.type != LED_ST_FLASH && led_state.type != LED_ST_FAST_FLASH) {
if (calibration_mode)
led_set(LED_ST_FAST_FLASH, 4);
else {
// Update led lighting
if (lock_mode)
led_set(LED_ST_CONST, 0);
else
led_set(LED_ST_OFF, 0);
}
}
// If !paired
if (calibration_mode) {
for (int i = 0; i < 4; ++i) {
if (calibration.stmn[i] > sticks[i])
calibration.stmn[i] = sticks[i];
if (calibration.stmx[i] < sticks[i])
calibration.stmx[i] = sticks[i];
}
} else {
// Sending package with sticks
Package pack;
pack.type = PACKAGE_STICKS;
pack.data.sticks.sticks[0] = sticks[0];
pack.data.sticks.sticks[1] = sticks[1];
pack.data.sticks.sticks[2] = sticks[2];
pack.data.sticks.sticks[3] = sticks[3];
bool result = send_package((byte*) &pack, sizeof(Package));
if (!result) {
if (tx_dropped > TX_DROP_EDGE && led_state.type != LED_ST_FLASH && led_state.type != LED_ST_FAST_FLASH)
led_set(LED_ST_FLASH, 4);
#ifdef DEBUG_PRINT
Serial.println("TX failed");
#endif
}
}
}
int send_package(byte* pack, int size) {
if (!radio.write(pack, size)) {
++tx_dropped;
return 0;
} else {
byte payload;
if (radio.isAckPayloadAvailable()) {
radio.read(&payload, sizeof(byte));
tx_dropped = 0;
return 1;
}
++tx_dropped;
return 0;
}
};
void led_set_state(int state) {
if (led_state.state && !state) {
digitalWrite(STLED, 0);
led_state.state = !led_state.state;
} else if (!led_state.state && state) {
analogWrite(STLED, LED_POWER);
led_state.state = !led_state.state;
}
};
void led_set(int type, int count) {
if (type == LED_ST_CONST || type == LED_ST_OFF) {
led_set_state(type == LED_ST_CONST);
led_state.type = type;
} else {
// if was flashing --> rewrite
// if was constant --> move type to ptype & do flashing
if (led_state.type == LED_ST_CONST || led_state.type == LED_ST_OFF)
led_state.ptype = led_state.type;
led_state.type = type;
led_state.count = count;
led_state.time = millis();
led_set_state(1);
}
};
void btn_act(int number) {
buttons.acted[number] = 1;
};
// Override calls for button presses
void btn_action(int number, int lpress) {
#ifdef DEBUG_PRINT
Serial.print("Press for "); Serial.println(number);
#endif
switch(number) {
// calibration
case 0: {
// press = ?
// Lpress = calibration mode
if (lpress) {
if (calibration_mode && !lock_mode) {
// Write calibration values
byte* cal_struct = (byte*) &calibration;
for (int i = 0; i < sizeof(STICK_CALIBRATION); ++i)
EEPROM.write(i, cal_struct[i]);
calibration_mode = 0;
#ifdef DEBUG_PRINT
Serial.println("NEW CALIBRATION: ");
for (int i = 0; i < 4; ++i) {
Serial.print('(');
Serial.print(calibration.stmn[i]);
Serial.print(", ");
Serial.print(calibration.stmx[i]);
Serial.print(") ");
}
Serial.println();
#endif
} else {
for (int i = 0; i < 4; ++i) {
calibration.stmn[i] = 1024;
calibration.stmx[i] = 0;
}
calibration_mode = 1;
}
}
break;
}
case 1: {
// press = ?
// Lpress = lock mode
if (lpress) {
lock_mode = !lock_mode;
}
break;
}
}
led_set(LED_ST_FAST_FLASH, 2);
btn_act(number);
Package pack;
pack.type = PACKAGE_BUTTON;
pack.data.button.number = number;
pack.data.button.lpress = lpress;
bool result = send_package((byte*) &pack, sizeof(Package));
#ifdef DEBUG_PRINT
if (!result)
Serial.println("Button TX failed");
#endif
};
Source for receiver is:
// Flash for self-diy NRF24lo* receiver controller.
// 6 buttons, 4-way sticks.
#include <Servo.h>
#include <SPI.h>
#include "RF24.h"
#define DEBUG_PRINT
#ifdef DEBUG_PRINT
#include <printf.h>
#endif
// IO mappings
#define CONNECT_0 2
#define CONNECT_1 3
#define CONNECT_2 4
#define CONNECT_3 5
#define CONNECT_4 6
#define CONNECT_5 7
#define CONNECT_6 8
#define CONNECT_A0 A0
#define CONNECT_A1 A1
#define CONNECT_A2 A2
#define CONNECT_A3 A3
#define CONNECT_A4 A4
#define CONNECT_A5 A5
// A6, A7 on nano are only analog input
#define CONNECT_A6 A6
#define CONNECT_A7 A7
// Inverters for sticks
#define INVERT_STICK0 0
#define INVERT_STICK1 0
#define INVERT_STICK2 0
#define INVERT_STICK3 0
// Mappings for sticks
#define SERVO_MAP_STICK0 0, 180
#define SERVO_MAP_STICK1 0, 180
#define SERVO_MAP_STICK2 0, 180
#define SERVO_MAP_STICK3 60, 120
#define ANALOG_MAP_STICK0 0, 255
#define ANALOG_MAP_STICK1 0, 255
#define ANALOG_MAP_STICK2 0, 255
#define ANALOG_MAP_STICK3 0, 255
// Optional moddings
// Package types
#define PACKAGE_STICKS 3
#define PACKAGE_BUTTON 5
#define PACKAGE_PING 7
// Used to detect disconnect from the controller
#define CONNECTION_TIMEOUT 100
// Mpdes for output of the motor
// #define MODE_2_DIGITAL_1_ANALOG
#define MODE_2_ANALOG
// Receiver package struct.
// Contains type and values for button or values for sticks
struct Package {
int type;
union {
struct {
int number;
int lpress;
// Used to complete size of the package.
// Without it, payload is not sending for sticks.
int dummy[2];
} button;
struct {
int sticks[4];
} sticks;
} data;
};
// Describes state of a single buttons on controller
// Single press -> 1
// Second press -> 0
struct Button {
bool state;
bool lstate;
};
// Use NRF24lo1 transmitter on pins 7, 8
RF24 radio(9, 10);
byte addresses[][6] = { "TRAAA", "REEEE" };
// Used to detect timed disconnection from the controller
unsigned long last_receive_time;
bool disconnected;
// Servos mapped to sticks
Servo servo[4];
// Buttons states
Button buttons[6];
// Called on connection restored after drop
void on_connection();
// Called on connection dropped
void on_disconnection();
// Called on button state received
void button_action(int button, int lpress);
// Called on sticks state received
void sticks_action(int sticks[4]);
void setPWMNanofrequency(int freq) {
TCCR2B = TCCR2B & 0b11111000 | freq;
TCCR1B = TCCR1B & 0b11111000 | freq;
}
void setup() {
#ifdef DEBUG_PRINT
Serial.begin(115200);
#endif
// Set up receiver
radio.begin();
radio.setChannel(77);
//radio.setAutoAck(1);
//radio.setRetries(0, 8);
radio.enableAckPayload();
radio.setPALevel(RF24_PA_MAX);
//radio.setCRCLength(RF24_CRC_8);
radio.openWritingPipe(addresses[0]);
radio.openReadingPipe(1, addresses[1]);
#ifdef DEBUG_PRINT
// Debugger output
printf_begin();
radio.printDetails();
#endif
radio.startListening();
// Init buttons with 0
buttons[0] = { 0, 0 };
buttons[1] = { 0, 0 };
buttons[2] = { 0, 0 };
buttons[3] = { 0, 0 };
buttons[4] = { 0, 0 };
buttons[5] = { 0, 0 };
// Set up pinout
pinMode(CONNECT_0, OUTPUT);
pinMode(CONNECT_2, OUTPUT);
pinMode(CONNECT_3, OUTPUT);
pinMode(CONNECT_4, OUTPUT);
pinMode(CONNECT_5, OUTPUT);
pinMode(CONNECT_6, OUTPUT);
pinMode(CONNECT_A0, OUTPUT);
pinMode(CONNECT_A1, OUTPUT);
pinMode(CONNECT_A2, OUTPUT);
pinMode(CONNECT_A3, OUTPUT);
pinMode(CONNECT_A4, OUTPUT);
pinMode(CONNECT_A5, OUTPUT);
pinMode(CONNECT_A6, INPUT);
pinMode(CONNECT_A7, INPUT);
// setPWMNanofrequency(0x02);
// Set up servos
servo[0].attach(CONNECT_A4); // Remapped servos to leave three PWM pins
servo[1].attach(CONNECT_A5);
servo[2].attach(CONNECT_5);
servo[3].attach(CONNECT_6);
// Clear disconnect trigger
last_receive_time = millis();
disconnected = 0;
// Reset outputs
digitalWrite(CONNECT_0, LOW);
digitalWrite(CONNECT_2, LOW);
digitalWrite(CONNECT_3, LOW);
digitalWrite(CONNECT_4, LOW);
digitalWrite(CONNECT_5, LOW);
digitalWrite(CONNECT_6, LOW);
digitalWrite(CONNECT_A0, LOW);
digitalWrite(CONNECT_A1, LOW);
digitalWrite(CONNECT_A2, LOW);
digitalWrite(CONNECT_A3, LOW);
digitalWrite(CONNECT_A4, LOW);
digitalWrite(CONNECT_A5, LOW);
// digitalWrite(CONNECT_A6, LOW);
// digitalWrite(CONNECT_A7, LOW);
}
void loop() {
byte payload = 13;
radio.writeAckPayload(1, &payload, sizeof(byte));
if (radio.available()) {
Package pack;
radio.read((byte*) &pack, sizeof(Package));
// Update trigger
last_receive_time = millis();
if (disconnected)
on_connection();
disconnected = 0;
switch(pack.type) {
case PACKAGE_STICKS: {
#ifdef DEBUG_PRINT
Serial.print(pack.data.sticks.sticks[0]); Serial.print(' ');
Serial.print(pack.data.sticks.sticks[1]); Serial.print(' ');
Serial.print(pack.data.sticks.sticks[2]); Serial.print(' ');
Serial.print(pack.data.sticks.sticks[3]); Serial.println();
#endif
if (INVERT_STICK0) pack.data.sticks.sticks[0] = 1024 - pack.data.sticks.sticks[0];
if (INVERT_STICK1) pack.data.sticks.sticks[1] = 1024 - pack.data.sticks.sticks[1];
if (INVERT_STICK2) pack.data.sticks.sticks[2] = 1024 - pack.data.sticks.sticks[2];
if (INVERT_STICK3) pack.data.sticks.sticks[3] = 1024 - pack.data.sticks.sticks[3];
int ssticks[4];
ssticks[0] = map(pack.data.sticks.sticks[0], 0, 1023, SERVO_MAP_STICK0);
ssticks[1] = map(pack.data.sticks.sticks[1], 0, 1023, SERVO_MAP_STICK1);
ssticks[2] = map(pack.data.sticks.sticks[2], 0, 1023, SERVO_MAP_STICK2);
ssticks[3] = map(pack.data.sticks.sticks[3], 0, 1023, SERVO_MAP_STICK3);
int asticks[4];
asticks[0] = map(pack.data.sticks.sticks[0], 0, 1023, ANALOG_MAP_STICK0);
asticks[1] = map(pack.data.sticks.sticks[1], 0, 1023, ANALOG_MAP_STICK1);
asticks[2] = map(pack.data.sticks.sticks[2], 0, 1023, ANALOG_MAP_STICK2);
asticks[3] = map(pack.data.sticks.sticks[3], 0, 1023, ANALOG_MAP_STICK3);
#ifdef DEBUG_PRINT
Serial.print("Servo data: ");
Serial.print(ssticks[0]); Serial.print(' ');
Serial.print(ssticks[1]); Serial.print(' ');
Serial.print(ssticks[2]); Serial.print(' ');
Serial.print(ssticks[3]); Serial.println();
Serial.print("Analog data: ");
Serial.print(asticks[0]); Serial.print(' ');
Serial.print(asticks[1]); Serial.print(' ');
Serial.print(asticks[2]); Serial.print(' ');
Serial.print(asticks[3]); Serial.println();
#endif
sticks_action(pack.data.sticks.sticks, ssticks, asticks);
break;
}
case PACKAGE_BUTTON: {
#ifdef DEBUG_PRINT
Serial.print(pack.data.button.number); Serial.print(' ');
Serial.print(pack.data.button.lpress); Serial.println();
#endif
// Update states of the buttons
if (pack.data.button.lpress)
buttons[pack.data.button.number].lstate = !buttons[pack.data.button.number].lstate;
else
buttons[pack.data.button.number].state = !buttons[pack.data.button.number].state;
button_action(pack.data.button.number, pack.data.button.lpress);
break;
}
}
} else if (!disconnected && millis() - last_receive_time > CONNECTION_TIMEOUT) {
disconnected = 1;
on_disconnection();
}
}
void on_connection() {
};
void on_disconnection() {
// servo[0].write(0);
// servo[1].write(0);
// servo[2].write(0);
// servo[3].write(0);
// analogWrite(CONNECT_A0, 0);
// analogWrite(CONNECT_A1, 0);
// analogWrite(CONNECT_A2, 0);
// analogWrite(CONNECT_A3, 0);
#ifdef MODE_2_DIGITAL_1_ANALOG
digitalWrite(CONNECT_1, LOW);
digitalWrite(CONNECT_2, LOW);
digitalWrite(CONNECT_3, LOW);
#endif
#ifdef MODE_2_ANALOG
digitalWrite(CONNECT_3, LOW);
digitalWrite(CONNECT_4, LOW);
digitalWrite(CONNECT_0, LOW);
#endif
};
void button_action(int button, int lpress) {
switch (button) {
case 0:
case 1:
break;
case 2: {
digitalWrite(CONNECT_A0, buttons[button].state);
break;
}
case 3: {
digitalWrite(CONNECT_A1, buttons[button].state);
break;
}
case 4: {
digitalWrite(CONNECT_A2, buttons[button].state);
break;
}
case 5: {
digitalWrite(CONNECT_A3, buttons[button].state);
break;
}
}
};
void sticks_action(int sticks[4], int ssticks[4], int asticks[4]) {
servo[0].write(ssticks[0]);
servo[1].write(ssticks[1]);
servo[2].write(ssticks[2]);
servo[3].write(ssticks[3]);
// analogWrite(CONNECT_A0, asticks[0]);
// analogWrite(CONNECT_A1, asticks[1]);
// analogWrite(CONNECT_A2, asticks[2]);
// analogWrite(CONNECT_A3, asticks[3]);
// Control H-Bridge over 3, 4 outputs with right stick
int value = (sticks[2] > 500) ? (sticks[2] - 500) : (sticks[2] < 480) ? (480 - sticks[2]) : (0);
if (value < 500)
value = map(value, 0, 480, 0, 255);
else
value = map(value, 0, 1023 - 500, 0, 255);
value = (value > 255) ? 255 : (value < 0) ? 0 : value;
// connecting 2 digital pins as HIGH,LOW / LOW,HIGH and analog as speed value (used in bts79603 bridge)
#ifdef MODE_2_DIGITAL_1_ANALOG
if (sticks[2] > 500) {
digitalWrite(CONNECT_1, HIGH);
digitalWrite(CONNECT_2, LOW);
analogWrite(CONNECT_3, value);
} else if (sticks[2] < 480) {
digitalWrite(CONNECT_1, LOW);
digitalWrite(CONNECT_2, HIGH);
analogWrite(CONNECT_3, value);
} else {
digitalWrite(CONNECT_1, LOW);
digitalWrite(CONNECT_2, LOW);
analogWrite(CONNECT_3, 0);
}
#endif
// connecting 2 analog pins to simple H-Bridge
#ifdef MODE_2_ANALOG
analogWrite(CONNECT_3, sticks[2] > 500 ? value : 0);
analogWrite(CONNECT_4, sticks[2] < 480 ? value : 0);
analogWrite(CONNECT_0, sticks[2] > 500 || sticks[2] < 480);
#endif
};
Transmitter printDetails output:
STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1 = 0x4141415254 0x4545454552
RX_ADDR_P2-5 = 0xc3 0xc4 0xc5 0xc6
TX_ADDR = 0x4141415254
RX_PW_P0-6 = 0x20 0x20 0x00 0x00 0x00 0x00
EN_AA = 0x3f
EN_RXADDR = 0x03
RF_CH = 0x4d
RF_SETUP = 0x07
CONFIG = 0x0e
DYNPD/FEATURE = 0x00 0x00
Data Rate = 1MBPS
Model = nRF24L01+
CRC Length = 16 bits
PA Power = PA_MAX
Receiver printDetails output:
STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1 = 0x4545454552 0x4141415254
RX_ADDR_P2-5 = 0xc3 0xc4 0xc5 0xc6
TX_ADDR = 0x4545454552
RX_PW_P0-6 = 0x20 0x20 0x00 0x00 0x00 0x00
EN_AA = 0x3f
EN_RXADDR = 0x02
RF_CH = 0x4d
RF_SETUP = 0x07
CONFIG = 0x0e
DYNPD/FEATURE = 0x00 0x00
Data Rate = 1MBPS
Model = nRF24L01+
CRC Length = 16 bits
PA Power = PA_MAX
Code used
Following code on receiver caused IO break.
pinMode(CONNECT_A6, INPUT);
pinMode(CONNECT_A7, INPUT);
A6, A7 pins DOES NOT support mode change and this issue made NRF24 connection to fail.
First of all, I copied/paste th code to see if it compliles in Arduino IDE, and it does, so I guess it's not a C++ related issue but maybe something more Atmel Studio or linker related.
I recently decided to switch from Arduino IDE to Atmel Studio to develop and program Arduino boards.
I have this project where I use a .h/.c library that I wrote, which is located in "../Lib". I have added the library with "Add existing item" and "Add as Link", because I wanted to work with the original code not a copy (a few things still need to be added to it)
A first it builded without any problem, but then I added an extra function in the library called "determineWinningPosition()". Now compilation returns an error :
D:\Google Drive\ISIB\5 - Mecatronic\Controller_v5_AS\Controller_v5_AS\Controller_v5\Sketch.cpp(76,28): error: 'determineWinningPositions' was not declared in this scope
determineWinningPositions();
The function prototype is declared in the library header "LetMeOut.h" :
#include <Arduino.h>
#include <Wire.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <EEPROM.h>
#include <stdlib.h>
// Key words
#define MOTOR1 (0)
#define MOTOR2 (1)
#define MAX_SPEED (15)
#define SIZE_RAND_SEQ (100)
#define BUTTON_TIMEOUT (10) // -> 1s
#define WINNING_POS_TOLERANCE (3)
#define GLOBAL_STATE_SIZE (7)
#define STATE0 (3)
#define STATE1 (2)
#define STATE2 (0)
#define STATE3 (1)
#define START (5)
#define STOP (10)
#define CHANGE_SPEED (15)
#define INIT_POS (20)
#define START_SPEED (25)
#define RESET_CMD (30)
#define GO_TO_POS (35)
#define UNLOCK_DOOR (40)
#define LOCK_DOOR (45)
#define CHANGE_WINNING_POS (50)
#define TRUE (1)
#define FALSE (0)
#define EEPROM_1 (0)
#define EEPROM_2 (1)
// Pins
#define DIR_M1 (A0)
#define DIR_M2 (A1)
#define EN_M1 (A2)
#define EN_M2 (A3)
#define SCL_PIN (A4)
#define SDA_PIN (A5)
#define STARTED_PIN (2)
#define A1_PIN (3)
#define B1_PIN (4)
#define A2_PIN (5)
#define CTL_M1 (6)
#define B2_PIN (7)
#define START_PIN (8)
#define STOP_M2 (9)
#define SUCCEED_PIN (10)
#define CTL_M2 (11)
#define STOP_M1 (12)
#define DOOR_PIN (13) // LED
// Prototypes
void Timer1_ISR(void);
void receiveEvent(int howMany);
void requestEvent();
void initI2C(void);
void initMotors(void);
void initEncoder(void);
void initDirectionTimer(void);
void setMotorSpeed(uint8_t motor, uint8_t motorSpeed, uint8_t dir);
void setAllMotorSpeed(uint8_t motorSpeed);
void stopMotor(uint8_t motorID);
void stopAllMotors (void);
void startMotor(uint8_t motorID);
void startAllMotors (void);
void buttonManager(void);
void directionManager(void);
void i2cCommandManager(void);
void printPosition(bool b);
void goToPosition(uint8_t position1_, uint8_t position2_);
void readEncoder(void);
void determineWinningPositions(void);
The function is implemented in "LetMeOut.cpp" :
#include "LetMeOut.h"
extern uint8_t directionChangeCounter1, directionChangeCounter2;
extern uint8_t directionChangeCounterTopValue1;
extern uint8_t directionChangeCounterTopValue2;
extern bool direction1;
extern bool direction2;
extern bool counterChanged;
extern uint8_t button1Counter;
extern bool button1CounterStarted;
extern uint8_t button2Counter;
extern bool button2CounterStarted;
extern volatile uint8_t startButtonCounter;
extern volatile bool startButtonCounterStarted;
extern uint8_t cmd[3];
extern uint8_t globalState[GLOBAL_STATE_SIZE];
extern bool newCmd;
const uint8_t randomSeq[SIZE_RAND_SEQ] = {9, 15, 8, 10, 10, 10, 17, 12, 6, 16, 6, 8, 14, 15, 19, 15, 9, 20, 13, 17, 7, 15, 13, 15, 5, 7, 10, 6, 8, 9, 14, 9, 19, 11, 15, 17, 17, 7, 18, 12, 8, 20, 12, 11, 6, 16, 19, 17, 16, 10, 14, 14, 18, 13, 5, 20, 17, 10, 17, 12, 15, 12, 16, 12, 12, 18, 18, 19, 13, 16, 6, 7, 16, 17, 12, 11, 19, 12, 19, 13, 15, 18, 5, 7, 8, 8, 16, 16, 8, 14, 17, 17, 13, 6, 6, 8, 7, 10, 20, 13};
extern uint8_t index1;
extern uint8_t index2;
extern uint8_t speedSetting;
extern volatile bool started;
extern bool initDone;
extern bool doorUnlocked;
extern volatile bool currentA1, currentB1, currentA2, currentB2;
extern volatile int currentState1, previousState1, currentState2, previousState2, position1, position2;
extern volatile bool printPos;
extern uint8_t winningPosition1;
extern uint8_t winningPosition2;
extern volatile bool succeed;
extern volatile bool motor1Started;
extern volatile bool motor2Started;
extern int* winningPositionList1;
extern int* winningPositionList2;
/************************************************************************************************/
/********************************** Interrupts and Callbacks ************************************/
/************************************************************************************************/
// Interrupt service routine called at every timer 1 overflow : every 100 ms
// Used to measure time between every speed changes and to implement a timeout every 1s to prevent
// the user from "machine gunning" the buttons
void Timer1_ISR(void)
{
// Increase both counters used for speed changes and notify the loop() there's been a change
directionChangeCounter1++;
directionChangeCounter2++;
counterChanged = 1;
// Prevent the user from "machine gunning" the buttons
if (button1CounterStarted)
{
button1Counter++;
if (button1Counter >= BUTTON_TIMEOUT)
{
button1CounterStarted = 0;
button1Counter = 0;
}
}
if (button2CounterStarted)
{
button2Counter++;
if (button2Counter >= BUTTON_TIMEOUT)
{
button2CounterStarted = 0;
button2Counter = 0;
}
}
// Debounce START_PIN
if (startButtonCounterStarted)
{
startButtonCounter++;
if (startButtonCounter >= 2)
{
startButtonCounterStarted = 0;
startButtonCounter = 0;
}
}
}
// Callback function that defines the behaviour in case of some received I2C bytes
void receiveEvent(int howMany)
{
// Read the incoming bytes
cmd[0] = Wire.read();
if (Wire.available())
cmd[1] = Wire.read();
if (Wire.available())
cmd[2] = Wire.read();
// Get rid of any other unread message
while (Wire.available() != 0)
{
Wire.read();
}
// Notify the loop() that there's a new command to be handled
newCmd = 1;
}
// Callback function that defines the behaviour in case of a received request
// for a global state update -> send the global state to the Rapsberry Pi
void requestEvent()
{
// Prepare the message to be sent
globalState[0] = started;
globalState[1] = succeed;
globalState[2] = speedSetting;
globalState[3] = doorUnlocked;
globalState[4] = initDone;
globalState[5] = winningPosition1;
globalState[6] = winningPosition2;
// Send the global state to the raspberry pi
for (int i=0; i<GLOBAL_STATE_SIZE; i++)
{
Wire.write(globalState[i]);
}
}
/************************************************************************************************/
/********************************** Initializations *********************************************/
/************************************************************************************************/
// Initialize the I2C
void initI2C(void)
{
Wire.begin(0x18);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
// Disable internal pullup
digitalWrite(SCL_PIN, LOW);
digitalWrite(SDA_PIN, LOW);
}
// Motor control related intializations
void initMotors(void)
{
// Initialize timer 0 to generate a square wave on OC0A (CTL_M1 - D6)
// Toggle OC0A on compare match (non-PWM mode), WF gen CTC mode
TCCR0A = _BV(COM0A0) | _BV(WGM21);
// Prescale = 1024 => period = 2 * 64us * (OCR0A+1);
TCCR0B = _BV(CS02) | _BV(CS00);
// Set initial value of compare (sets the frequency)
OCR0A = MAX_SPEED;
// => Changing OCR0A value will set frequency of the square wave and thus the motors speed
// Initialize timer 2 to generate a square wave on OC2A (CTL_M2 - D11)
// Toggle OC2B on compare match (non-PWM mode), WF gen CTC mode
TCCR2A = _BV(COM2A0) | _BV(WGM21);
// Prescale = 1024 => period = 2 * 64us * (OCR2A+1);
TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);
// Set initial value of compare (sets the frequency)
OCR2A = MAX_SPEED;
// => Changing OCR0A value will set frequency of the square wave and thus the motors speed
// Set the correct modes and initialize all the pins that are necessary for motor control
pinMode(CTL_M1, OUTPUT);
pinMode(CTL_M2, OUTPUT);
pinMode(DIR_M1, OUTPUT);
pinMode(DIR_M2, OUTPUT);
pinMode(EN_M1, OUTPUT);
digitalWrite(EN_M1, LOW);
pinMode(EN_M2, OUTPUT);
digitalWrite(EN_M2, LOW);
}
// Initialize pins and interrupts for the encoder
void initEncoder(void)
{
// Configure pin change interrupts (PCINT) on A1, B1, A2 and B2
// -> PCIE2 group
PCICR = _BV(PCIE2);
// Configure PCIE2 interrupt mask
PCMSK2 = _BV(PCINT19) | _BV(PCINT20) | _BV(PCINT21) | _BV(PCINT23);
pinMode(A1_PIN, INPUT);
pinMode(B1_PIN, INPUT);
pinMode(A2_PIN, INPUT);
pinMode(B2_PIN, INPUT);
}
// Initialize timer1 to overflow every 100 ms (used for timing related things)
void initDirectionTimer(void)
{
// Timer 1 config : normal mode
TCCR1A = 0;
// Prescaler 64 ; CTC mode
TCCR1B = _BV(CS11) | _BV(CS10) | _BV(WGM12);
TIMSK1 = 0b00000010;
// Initiate Timer 1 and compare A value
TCNT1 = 0;
OCR1A = 25000; // overflow every 100ms
}
/************************************************************************************************/
/****************************** Motor-related functions *****************************************/
/************************************************************************************************/
// Set the speed of a particular motor
void setMotorSpeed(uint8_t motor, uint8_t motorSpeed, uint8_t dir)
{
// Make sure the speed isn't too fast
if (motorSpeed < MAX_SPEED)
motorSpeed = MAX_SPEED;
// Set the speed by setting OCRxA and the direction by setting DIR_Mx pin
if (motor == MOTOR1)
{
OCR0A = motorSpeed;
digitalWrite(DIR_M1, dir);
}
else if (motor == MOTOR2)
{
OCR2A = motorSpeed;
digitalWrite(DIR_M2, dir);
}
}
// Set the speed of both motors at the same time
void setAllMotorSpeed(uint8_t motorSpeed)
{
// Make sure the speed isn't too fast
if (motorSpeed < MAX_SPEED)
motorSpeed = MAX_SPEED;
OCR0A = motorSpeed;
OCR2A = motorSpeed;
}
// Stop a particular motor by setting its "Not_EN" pin
void stopMotor(uint8_t motorID)
{
if (motorID == MOTOR1)
{
digitalWrite(EN_M1, HIGH);
}
else
{
digitalWrite(EN_M2, HIGH);
}
}
// Stop all motors by setting their "Not_EN" pin
void stopAllMotors (void)
{
digitalWrite(EN_M1, HIGH);
digitalWrite(EN_M2, HIGH);
}
// Start a particular motor by clearing its "Not_EN" pin
void startMotor(uint8_t motorID)
{
if (motorID == MOTOR1)
{
digitalWrite(EN_M1, LOW);
}
else
{
digitalWrite(EN_M2, LOW);
}
}
// Start all motors by clearing their "Not_EN" pin
void startAllMotors (void)
{
digitalWrite(EN_M1, LOW);
digitalWrite(EN_M2, LOW);
}
/************************************************************************************************/
/********************************** Other functions *********************************************/
/************************************************************************************************/
// Check is the player has pushed the button to stop the needles
// and implement corresponding behavior
void buttonManager(void)
{
// Has player pushed button 1 ?
if (!digitalRead(STOP_M1))
{
// If we're not in a button timeout period, stop the motors
if (!button1CounterStarted)
{
stopMotor(MOTOR1);
motor1Started = 0;
button1CounterStarted = 1;
}
}
else
{
if (!button1CounterStarted)
{
startMotor(MOTOR1);
motor1Started = 1;
}
// Keep the motor stopped during the timeout period
else
{
stopMotor(MOTOR1);
motor1Started = 0;
}
}
// Has player pushed button 2 ?
if (!digitalRead(STOP_M2))
{
// If we're not in a button timeout period, stop the motors
if (!button2CounterStarted)
{
stopMotor(MOTOR2);
motor2Started = 0;
button2CounterStarted = 1;
}
}
else
{
if (!button2CounterStarted)
{
startMotor(MOTOR2);
motor2Started = 1;
}
// Keep the motor stopped during the timeout period
else
{
stopMotor(MOTOR2);
motor2Started = 0;
}
}
if (!digitalRead(STOP_M1) && !digitalRead(STOP_M2))
{
bool won1 = 0, won2 = 0;
// Check if needle 1 is in a winning position
for (int i=0; i<2*WINNING_POS_TOLERANCE+1; i++)
{
if (position1 == winningPositionList1[i])
{
won1 = 1;
break;
}
}
// Check if needle 2 is in a winning position
for (int i=0; i < 2*WINNING_POS_TOLERANCE+1; i++)
{
if (position2 == winningPositionList2[i])
{
won2 = 1;
break;
}
}
// If both are winnin, the player won
if (won1 && won2)
{
digitalWrite(DOOR_PIN, HIGH);
doorUnlocked = TRUE;
Serial.println("WINNER");
succeed = 1;
started = 0;
digitalWrite(STARTED_PIN, started);
digitalWrite(SUCCEED_PIN, succeed);
// Make sure all motors are stopped (in case the switches has been pushed between the previous "if" and this one
// (happened once...)
stopAllMotors();
}
}
}
// Manages the direction changes
// This function is called whenever on of the direction change counter value has changed
void directionManager(void)
{
uint8_t newSpeed;
// Check if our counter has overflowed
if (directionChangeCounter1 >= directionChangeCounterTopValue1)
{
// Switch the direction
direction1 = !direction1;
// Reset the counter
directionChangeCounter1 = 0;
// Find the next value in the randomSeq array -> time intervals are random
index1 = (index1+1)%SIZE_RAND_SEQ;
directionChangeCounterTopValue1 = randomSeq[index1];
// Modify the speed according to the time value
// long time value <=> faster speed
// short time value <=> slower speed
if (directionChangeCounterTopValue1 < 10)
newSpeed = speedSetting + 4;
else if (directionChangeCounterTopValue1 < 15)
newSpeed = speedSetting + 2;
else
newSpeed = speedSetting;
setMotorSpeed(MOTOR1, newSpeed, direction1);
}
// Same as for motor 1...
if (directionChangeCounter2 >= directionChangeCounterTopValue2)
{
direction2 = !direction2;
directionChangeCounter2 = 0;
index2 = (index2+1)%SIZE_RAND_SEQ;
directionChangeCounterTopValue2 = randomSeq[index2];
if (directionChangeCounterTopValue2 < 10)
newSpeed = speedSetting + 4;
else if (directionChangeCounterTopValue2 < 15)
newSpeed = speedSetting + 2;
else
newSpeed = speedSetting;
setMotorSpeed(MOTOR2, newSpeed, direction2);
}
}
// Defines how the uC reacts to i2c commands from raspberry pi server
void i2cCommandManager(void)
{
// Did we get any command from the server ?
if (newCmd)
{
// Serial.print("i2c = ");
// Serial.print(cmd[0]);
// Serial.print(" ");
// Serial.print(cmd[1]);
// Serial.print(" ");
// Serial.println(cmd[2]);
switch (cmd[0])
{
case CHANGE_SPEED : // Change the speed
speedSetting = cmd[1];
setAllMotorSpeed(speedSetting);
Serial.print("speed = ");
Serial.println(speedSetting);
break;
case START : // Start and reset the game (unused, replaced by START_SPEED)
startAllMotors();
started = 1;
succeed = 0;
digitalWrite(STARTED_PIN, started);
digitalWrite(SUCCEED_PIN, succeed);
motor1Started = 1;
motor2Started = 1;
digitalWrite(DOOR_PIN, LOW);
doorUnlocked = FALSE;
Serial.println("START");
break;
case STOP : // Stop the game
stopAllMotors();
started = 0;
digitalWrite(STARTED_PIN, started);
motor1Started = 0;
motor2Started = 0;
Serial.println("STOP");
break;
case INIT_POS : // Calibrate the encoder according to the needles positions the user typed on the html form
position1 = cmd[1];
position2 = cmd[2];
initDone = TRUE;
Serial.print("Init Pos = ");
Serial.print(position1);
Serial.print(" ");
Serial.println(position2);
break;
case START_SPEED : // Reset, start and set the speed
startAllMotors();
started = 1;
succeed = 0;
digitalWrite(STARTED_PIN, started);
digitalWrite(SUCCEED_PIN, succeed);
motor1Started = 1;
motor2Started = 1;
digitalWrite(DOOR_PIN, LOW);
doorUnlocked = FALSE;
Serial.print("START SPEED = ");
speedSetting = cmd[1];
setAllMotorSpeed(speedSetting);
Serial.print("speed = ");
Serial.println(speedSetting);
break;
case RESET_CMD : // Reset the game without starting it
stopAllMotors();
succeed = 0;
started = 0;
digitalWrite(STARTED_PIN, started);
digitalWrite(SUCCEED_PIN, succeed);
motor1Started = 0;
motor2Started = 0;
digitalWrite(DOOR_PIN, LOW);
doorUnlocked = FALSE;
Serial.println("RESET");
break;
case GO_TO_POS : // Make the needles go to a certain position
Serial.print("Go to pos ");
Serial.print(cmd[1]);
Serial.print(" ");
Serial.println(cmd[2]);
goToPosition(cmd[1], cmd[2]);
break;
case UNLOCK_DOOR : // Unlock the door
digitalWrite(DOOR_PIN, HIGH);
doorUnlocked = TRUE;
Serial.println("Unlock the door");
break;
case LOCK_DOOR : // Lock the door
digitalWrite(DOOR_PIN, LOW);
doorUnlocked = FALSE;
Serial.println("Lock the door");
break;
case CHANGE_WINNING_POS : // Redefine the winning position, which is written on the EEPROM to keep it when the uC has been stopped
winningPosition1 = cmd[1];
winningPosition2 = cmd[2];
EEPROM.write(EEPROM_1, winningPosition1);
EEPROM.write(EEPROM_2, winningPosition2);
determineWinningPositions();
Serial.print("Change win. pos. to (");
Serial.print(cmd[1]);
Serial.print(",");
Serial.print(cmd[2]);
Serial.println(")");
break;
}
newCmd = 0;
}
}
// Print the current needles position for debug purposes
void printPosition(bool b)
{
if (printPos && b)
{
Serial.print(position1);
Serial.print(" ");
Serial.println(position2);
printPos = 0;
}
}
// Make the needles go to the position the user submitted on the html form
void goToPosition(uint8_t position1_, uint8_t position2_)
{
// Set the speed pretty slow and make sure the motors are started
setAllMotorSpeed(45);
startAllMotors();
bool okPos1 = 0, okPos2 = 0;
// turn the needles clockwise until the both reach the desired position
while(!(okPos1 && okPos2))
{
if (position1 == position1_)
{
stopMotor(MOTOR1);
okPos1 = 1;
}
if (position2 == position2_)
{
stopMotor(MOTOR2);
okPos2 = 1;
}
}
setAllMotorSpeed(speedSetting);
}
// Determine the new motor position when there's been a change on the encoder pins
void readEncoder(void)
{
// The binary combinations of A1 and B1 is always, in decimal :
// 3 2 0 1 3 2 0 1 3 2 0 1 ... when the motor is rotating clockwise
// 1 0 2 3 1 0 2 3 1 0 2 3 ... when the motor is rotating anticlockwise
// Knowing this we can deduce from A1-B1 combinations which way the motor is turning
currentA1 = digitalRead(A1_PIN);
currentB1 = digitalRead(B1_PIN);
// Serial.print(currentA1);
// Serial.print(currentB1);
// Serial.print(" ");
currentState1 = ((char)currentA1<<1) + currentB1;
if (currentState1 != previousState1)
{
if (currentState1 == STATE0)
{
if (previousState1 == STATE3)
{
position1++;
if (position1 == 60)
position1 = 0;
}
else if (previousState1 == STATE1)
{
position1--;
if (position1 == -1)
position1 = 59;
}
}
if (currentState1 == STATE1)
{
if (previousState1 == STATE0)
{
position1++;
if (position1 == 60)
position1 = 0;
}
else if (previousState1 == STATE2)
{
position1--;
if (position1 == -1)
position1 = 59;
}
}
if (currentState1 == STATE2)
{
if (previousState1 == STATE1)
{
position1++;
if (position1 == 60)
position1 = 0;
}
else if (previousState1 == STATE3)
{
position1--;
if (position1 == -1)
position1 = 59;
}
}
if (currentState1 == STATE3)
{
if (previousState1 == STATE2)
{
position1++;
if (position1 == 60)
position1 = 0;
}
else if (previousState1 == STATE0)
{
position1--;
if (position1 == -1)
position1 = 59;
}
}
previousState1 = currentState1;
printPos = 1;
}
currentA2 = digitalRead(A2_PIN);
currentB2 = digitalRead(B2_PIN);
// Serial.print(currentA2);
// Serial.println(currentB2);
currentState2 = ((char)currentA2<<1) + currentB2;
if (currentState2 != previousState2)
{
if (currentState2 == STATE0)
{
if (previousState2 == STATE3)
{
position2++;
if (position2 == 60)
position2 = 0;
}
else if (previousState2 == STATE1)
{
position2--;
if (position2 == -1)
position2 = 59;
}
}
if (currentState2 == STATE1)
{
if (previousState2 == STATE0)
{
position2++;
if (position2 == 60)
position2 = 0;
}
else if (previousState2 == STATE2)
{
position2--;
if (position2 == -1)
position2 = 59;
}
}
if (currentState2 == STATE2)
{
if (previousState2 == STATE1)
{
position2++;
if (position2 == 60)
position2 = 0;
}
else if (previousState2 == STATE3)
{
position2--;
if (position2 == -1)
position2 = 59;
}
}
if (currentState2 == STATE3)
{
if (previousState2 == STATE2)
{
position2++;
if (position2 == 60)
position2 = 0;
}
else if (previousState2 == STATE0)
{
position2--;
if (position2 == -1)
position2 = 59;
}
}
previousState2 = currentState2;
printPos = 1;
}
}
void determineWinningPositions(void)
{
int firstPos = winningPosition1-WINNING_POS_TOLERANCE;
if (firstPos < 0)
firstPos =+ 60;
for (int i=0; i<(2*WINNING_POS_TOLERANCE+1); i++)
{
winningPositionList1[i] = (firstPos+i)%60;
}
firstPos = winningPosition2-WINNING_POS_TOLERANCE;
if (firstPos < 0)
firstPos =+ 60;
for (int i=0; i<(2*WINNING_POS_TOLERANCE+1); i++)
{
winningPositionList2[i] = (firstPos+i)%60;
}
}
The header is included at the beginning of "Sketch.cpp" :
#include <Arduino.h>
#include "LetMeOut.h"
ISR(TIMER1_COMPA_vect );
ISR(PCINT2_vect );
bool debug = 0;
uint8_t directionChangeCounter1 = 0, directionChangeCounter2 = 0;
uint8_t directionChangeCounterTopValue1 = 10;
uint8_t directionChangeCounterTopValue2 = 10;
bool direction1 = 0;
bool direction2 = 0;
bool counterChanged = 0;
uint8_t button1Counter = 0;
bool button1CounterStarted = FALSE;
uint8_t button2Counter = 0;
bool button2CounterStarted = FALSE;
volatile uint8_t startButtonCounter = 0;
volatile bool startButtonCounterStarted = FALSE;
uint8_t cmd[3] = {0,0,0};
uint8_t globalState[GLOBAL_STATE_SIZE];
bool newCmd = 0;
uint8_t index1 = 0;
uint8_t index2 = 64;
uint8_t speedSetting = MAX_SPEED;
volatile bool started = 0;
bool initDone = FALSE;
bool doorUnlocked = FALSE;
volatile bool currentA1, currentB1, currentA2, currentB2;
volatile int currentState1, previousState1, currentState2, previousState2, position1, position2;
volatile bool printPos = 0;
uint8_t winningPosition1;
uint8_t winningPosition2;
volatile bool succeed = 0; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
volatile bool motor1Started = 0;
volatile bool motor2Started = 0;
int* winningPositionList1;
int* winningPositionList2;
void setup()
{
Serial.begin(2000000);
Serial.println("****************************************************************");
Serial.println("******************** L'horloge déjantée ************************");
Serial.println("************* Eric Bohnes - ISIB 2017-2018**********************");
Serial.println("****************** Master 2 Electronique ***********************");
Serial.println("****************** Cours de Mécatronique ***********************");
Serial.println("*****Collaboration avec l'Escape Game LET ME OUT Bruxelles *****");
Serial.println("****************************************************************\n\n");
initMotors();
initDirectionTimer();
initI2C();
initEncoder();
// Set the player's STOP buttons on INPUT_PULLUP
pinMode(STOP_M1, INPUT_PULLUP);
pinMode(STOP_M2, INPUT_PULLUP);
pinMode(SUCCEED_PIN, OUTPUT);
pinMode(STARTED_PIN, OUTPUT);
// Enable global interrupts
interrupts();
pinMode(START_PIN, INPUT_PULLUP);
stopAllMotors();
winningPosition1 = EEPROM.read(EEPROM_1);
winningPosition2 = EEPROM.read(EEPROM_2);
digitalWrite(STARTED_PIN, started);
digitalWrite(SUCCEED_PIN, succeed);
winningPositionList1 = (int*)malloc((2*WINNING_POS_TOLERANCE+1)*sizeof(int));
winningPositionList2 = (int*)malloc((2*WINNING_POS_TOLERANCE+1)*sizeof(int));
determineWinningPositions();
}
void loop()
{
// If the player didn't succeed yet, check we must change a direction or if the player pushed a button
if (!succeed)
{
// Check if it's time to change direction
if (counterChanged)
directionManager();
// If the game is started, poll the buttons
if (started)
buttonManager();
// Set the parameter TRUE for debugging purposes
printPosition(FALSE);
}
i2cCommandManager();
if (!digitalRead(START_PIN))
{
if (!startButtonCounterStarted)
{
startAllMotors();
started = 1;
succeed = 0;
digitalWrite(STARTED_PIN, started);
digitalWrite(SUCCEED_PIN, succeed);
motor1Started = 1;
motor2Started = 1;
doorUnlocked = FALSE;
digitalWrite(DOOR_PIN, LOW);
Serial.println("Started by pin");
startButtonCounterStarted = TRUE;
startButtonCounter = 0;
}
}
}
ISR(TIMER1_COMPA_vect)
{
Timer1_ISR();
}
ISR(PCINT2_vect)
{
readEncoder();
}
As I said before, I copied/pasted the sketch in Arduino IDE and it compiles without any errors with this tool. When trying to compile with Atmel Studio, I tried to build/rebuild the solution, the sketch itself, with and without cleaning first, still get the error.
Here is a solution explorer printscreen :
Any clue would be appreciated !
Best regards.
Eric
I see a lot of discussion on this error, but I can't seem to find a format that works. I am getting the following error:
In member function 'void radarClass::ping()':
error: no matching function for call to 'NewPing::ping_timer(<unresolved overloaded function type>)'
note: candidates are: void NewPing::ping_timer(void (*)())
My full code is attached below, the error is at line 198:
sonar->ping_timer(echoCheck);
I have also unsuccessfully tried:
sonar->ping_timer((void* ) &radarClass::echoCheck));
sonar->ping_timer(&radarClass::echoCheck);
Full code here:
#ifndef RADAR_CLASS
#define RADAR_CLASS
#include <NewPing.h>
#include <Servo.h>
#include <PString.h>
/*
**
** MOVING AVERAGE CLASS
** Special volatile impelentation, fixed Int type
**
*/
template <int N> class volatileMovingAverage
{
public:
volatileMovingAverage(int def = 0) : sum(0), p(0)
{
for (int i = 0; i < N; i++) {
samples[i] = def;
sum += samples[i];
}
}
int add(int new_sample)
{
sum = sum - samples[p] + new_sample;
samples[p++] = new_sample;
if (p >= N)
p = 0;
return sum / N;
}
int inline getAverage(void)
{
return sum / N;
}
private:
volatile int samples[N];
volatile int sum;
volatile int p;
}; // Class volatileMoving Average
/* *************************************************************** */
// RadarClass
//
// USAGE:
// ----------------------
// NewPing mySonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
// Servo myServo;
// radarClass myRadar(&myServo, &mySonar);
// myRadar.attachServo(pinNumber);
// myRadar.setServoSpeed(200); // 0.2 seconds/60 degrees of rotation
// myRadar.run(); etc.
#define RADAR_DEBUG_MODE YES // for testing only, comment out otherwise
#define NO_ANGLES 9 // number of angles, i.e. positions at which to take measurements
class radarClass
{
private:
int maxAngle; // 9 values, positions 0 - 8
int angles[NO_ANGLES]; // values: {0, 22, 44, 67, 90, 112, 135, 157, 180}
volatileMovingAverage<4> sonarAverage[NO_ANGLES]; // array of moving average distances (one per angle)
volatile uint8_t pos; // current position within angle array
unsigned long int servoArrivalTime; // time (millis) when servo will complete its rotation and arrive at next position
int direction; // direction traversing the angle array (+1 or -1)
Servo *radarServo; // pointer to the servo
NewPing *sonar; // point to the NewPing sensor
int maxDist; // max distance (used by NewPing sensor)
int servoSpeed; // servo peed in milli-seconds to move 60 degrees (e.g. 0.15 seconds to move 60 degrees of rotation = 0.15 x 1000 = 150)
volatile bool waitingForPing; // flag to mark when ping result received
bool pingStarted; // flag to keep track of ping loop status
volatile int pingValue; // used to pass ping result between the two routines/ISRs
void nextPosition(void);
void ping(void);
int getMinDistance(int start_pos, int end_pos);
void commonInit(void);
void echoCheck(void);
public:
radarClass(Servo *svr, NewPing *snr);
radarClass(NewPing *snr, Servo *svr);
int getMinDistanceLeft(void);
int getMinDistanceRight(void);
int getMinDistasnceFront(void);
void run(void);
void attachServo(int pin);
void setServoSpeed(int);
void setMaxDistance(int d) {maxDist = d;};
#ifdef RADAR_DEBUG_MODE
void printAllValues(void);
void printInt(int val, int len);
#endif
//int getCurrentAngle(void) {return angles[pos];}
//int getDistance(int p) {return sonarAverage[p].getAverage();}
}; // radarClass
/* ********************************************************** */
// constructor
radarClass::radarClass(Servo *svr, NewPing *snr)
{
// save the passed pointers
radarServo = svr;
sonar = snr;
// run common initialization routine
commonInit();
} // radarClass::radarClass constructor
/* ********************************************************** */
// constructor
radarClass::radarClass(NewPing *snr, Servo *svr)
{
// save the passed pointers
radarServo = svr;
sonar = snr;
// run common initialization routine
commonInit();
} // radarClass::radarClass constructor
/* ********************************************************** */
// constructor common initialization routine
void radarClass::commonInit(void)
{
// initialize variables
maxAngle = NO_ANGLES - 1;
pos = 0;
direction = 1;
servoSpeed = 100; // default to 0.10 s/60 degrees (x 1000 per millisecond) for TowerPro SG-90
maxDist = 100; // max distance usable by NewPing IN INCHES
pingStarted = false;
waitingForPing = false;
// initialize the angle array...not elegant but it works
// angles[] = {0, 22, 44, 67, 90, 112, 135, 157, 180};
angles[0] = 0;
angles[1] = 22;
angles[2] = 44;
angles[3] = 67;
angles[4] = 90;
angles[5] = 112;
angles[6] = 135;
angles[7] = 157;
angles[8] = 180;
// move servo to initial position
radarServo->write(angles[0]);
// set arrival time (estimated)
servoArrivalTime = millis() + (abs(angles[0] - angles[maxAngle]) * servoSpeed / 60 / 2);
}
/* ********************************************************** */
// main external program loop...call continually, moves servo & reads values
void radarClass::run(void)
{
unsigned long int t = millis();
// has the servo arrived at target angle yet?
if (t < servoArrivalTime)
return;
// read distance at current location, wait for result, and advance to next location
ping();
}
/* ********************************************************** */
// get ping distance at current servo position
void radarClass::ping(void)
{
if (!pingStarted) // send new ping, echoCheck will be called by interrupt Timer2 until result received
{
pingStarted = true;
waitingForPing = true;
sonar->ping_timer(echoCheck);
return;
}
else // pingStarted loop is true, already called ping timer...
{
if (!waitingForPing) // have we received the result back?
{
if (pingValue == 0) // NewPing returns 0 for undetactable ranges...
pingValue = maxDist;
sonarAverage[pos].add( pingValue );
pingStarted = false; // toggle flag to mark that we are ot of hte loop
nextPosition(); // advance to the next position
}
}
}
/* ********************************************************** */
// function called by NewPing ISR to check for ping result;
// Timer2 interrupt calls this function every 24uS where you can check the ping status.
void radarClass::echoCheck(void)
{
// Don't do anything here!
if (sonar->check_timer()) // Check to see if the ping was received.
{
pingValue = (sonar->ping_result / US_ROUNDTRIP_IN); // Ping returned, uS result in ping_result, convert to inches with US_ROUNDTRIP_IN.
waitingForPing = false; // toggle flag so we know result is complete
}
// Don't do anything here!
}
/* ********************************************************** */
// move servo to the next position
void radarClass::nextPosition(void)
{
// if at either end, change direction
if (pos == 0)
direction = 1;
else if (pos == maxAngle)
direction = -1;
// advance to next position
pos += direction;
radarServo->write(angles[pos]);
// calculate arrival time at next position
servoArrivalTime = millis() + ( (abs(angles[pos] - angles[pos + direction]) * servoSpeed ) / 60);
}
/* ********************************************************** */
// set the servo speed, in milliseconds per 60 degrees
void radarClass::setServoSpeed(int spd)
{
servoSpeed = spd;
}
/* ********************************************************** */
// Attach servo to specified pin
void radarClass::attachServo(int pin)
{
radarServo->attach(pin);
}
/* ********************************************************** */
// return the longest distance for given array entries (inclusive)
int radarClass::getMinDistance(int start_pos, int end_pos)
{
// to make sure underlying data isn't changed by ISR routine, briefly turn off interrupts
//noInterrupts();
// calculate the min value
int smallest = sonarAverage[start_pos].getAverage();
for (int t = start_pos + 1; t <= end_pos; t++)
{
int curr = sonarAverage[t].getAverage();
if (curr < smallest)
smallest = curr;
}
// turn interrupts back on
//interrupts();
// return the calculated value
return (smallest);
}
/* ********************************************************** */
// return closest distance to left (positions 0, 1 & 2)
int radarClass::getMinDistanceLeft(void)
{
return getMinDistance(0, 2);
}
/* ********************************************************** */
// return closest distance to right (positions 6, 7 & 8)
int radarClass::getMinDistanceRight(void)
{
return getMinDistance(6, 8);
}
/* ********************************************************** */
// return closest distance to front (positions 3, 4 & 5)
int radarClass::getMinDistasnceFront(void)
{
return getMinDistance(3, 5);
}
// code for debugging; prints all values in neat format
#ifdef RADAR_DEBUG_MODE
void radarClass::printAllValues(void)
{
static bool firstEntry = true;
if (firstEntry) // only print the header row once
{
Serial.println(" ");
Serial.println(F("Angle 1 Angle 2 Angle 3 Angle 4 Angle 5 Angle 6 Angle 7 Angle 8 Angle 9"));
for (int i = 0; i <= maxAngle; i++) // print the angles
{
printInt(angles[i], 7);
Serial.print(" ");
}
Serial.println("");
Serial.println(F("------- ------- ------- ------- ------- ------- ------- ------- -------"));
firstEntry = false;
}
// print the distances
for (int i = 0; i <= maxAngle; i++)
{
printInt(sonarAverage[i].getAverage(), 7);
Serial.print(" ");
}
Serial.println("");
} // void printAllValues(void)
/* ********************************************************** */
// Print integer, padded for alignment
void radarClass::printInt(int val, int len)
{
int strLen;
char buffer[30];
PString str(buffer, sizeof(buffer));
// Find the length of the printed value
str.begin();
str.print(val);
strLen = str.length();
str.begin();
// Pad string with leading spaces
for (int t = len - strLen; t > 0; t--)
{
str += " ";
}
// then print the string
str.print(val);
//str += " ";
// print result
Serial.print(str);
} // void printInt(int val, int len)
#endif // RADAR_DEBUG_MODE
#endif // RADAR_CLASS
/* ****************************************************************************************** */
/*
** TEST CODE HERE
********************************
*/
//#include <TimerOneThree.h>
#include <TimerOne.h>
#define RADAR_INT_TIME 100000 // 100,000 microseconds = 10x/second = 1 sweep of 9 angles per sceond
Servo myServo;
NewPing mySonar(10, 11, 250);
radarClass myRadar(&myServo, &mySonar);
// function to be called by the interrupt timer; will run the Radar code
void intRunRadar(void)
{
myRadar.run();
} // void intRunRadar()
void setupRadarInterrupt(void)
{
// Timer1.initialize(RADAR_INT_TIME); // set how often to run the interrupt (in microseconds)
// Timer1.attachInterrupt(intRunRadar);
} // void setupRadarInterrupt(void)
void setup()
{
Serial.begin(9600);
myRadar.attachServo(3);
myRadar.setServoSpeed(150);
delay(500);
//setupRadarInterrupt();
}
void loop()
{
unsigned long int t1, t2;
t1 = millis();
Serial.println("Starting test");
while (1)
{
myRadar.run();
t2 = millis();
if (t2 >= t1 + 1000)
{
t1 = t2;
myRadar.printAllValues();
}
} // while
} // loop()
I have apparently reached the limit of my knowledge here.
Add constructors to the sketch:
#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); //