[Edit: I've added a diagram because the image I uploaded doesn't line up with the rows of the bread board because of the perspective]
I'm trying to write program code to Read and Write to an external Serial Flash Rom (a AT25SF081B) via SPI with an Arduino Nano Every. My function for reading from a page of memory on the ROM works as it returns all 1s since the Flash is blank. I've checked the contents of the buffers sent via the function param and its taking what I input but its not writing to the ROM.
How I have it wired, the AT25SF081B is on a breakout board that numbered differently then the package, its mirrored:
Breakout AT25SF081B
8| u |1 1| u |8
7| |2 2| |7
6| |3 3| |6
5| |4 4| |5
My sketch, I was following this tutorial But there have been incompatibilities because I'm using an Nano Every, not sure if that is partly why it doesn't work? I've been scouring the internet for answers but can't find anything because there is more tailored to the Uno then the Nano Every.
#include <SPI.h>
#define DATAOUT 14 //MOSI
#define DATAIN 15 //MISO
#define SPICLOCK 16 //SCK
#define CHIPSELECT 26 // CS
//#define BASEADDRESS 000000h
//#define ENDADDRESS 0FFFFFh
//opcodes
#define WREN 0x06
#define WRDI 0x04
#define RDSR 0x05
#define READ 0x03
#define PAGE_PROG 0x02
#define ERASE 0x60
#define JEDEC 0x9f
//globals
boolean isReady(false);
String command;
//data buffer
byte output [256];
void(* resetFunc) (void) = 0;
void printPage(byte *page_buffer) {
//Serial.println("printPage called");
char buff[10];
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
sprintf(buff, "%02x", page_buffer[i * 16 + j]);
Serial.print(buff);
}
Serial.println();
}
}
void getInfo(void) {
Serial.println("command: getInfo");
byte *b1, *b2, *b3;
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(JEDEC);
*b1 = SPI.transfer(0); // manufacturer id 0x1c
*b2 = SPI.transfer(0); // memory type 0x31
*b3 = SPI.transfer(0); // capacity 0x14
digitalWrite(CHIPSELECT, HIGH);
//toggleCS();
Serial.println("toggleCS exited");
char buff[128];
sprintf(buff, "Manufacturer ID: %02xh\nMemory Type: %02xh\nCapacity: %02xh",
&b1, &b2, &b3);
Serial.println(buff);
Serial.println("Ready");
}
void erase(void)
{
Serial.println("Command: Erase Chip");
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(WREN);
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(ERASE);
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
toggleCS();
Serial.println("Ready");
}
void readPage(unsigned int page_number)
{
char buff[80];
sprintf(buff, "Command: Read (%04xh)", page_number);
Serial.println(buff);
byte page_buffer[256];
getPage(page_number, page_buffer);
//printPage(output);
Serial.println("READY");
}
void getPage(word page_number, byte *page_buffer)
{
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(READ);
SPI.transfer((page_number >> 8) & 0xFF);
SPI.transfer((page_number >> 0) & 0xFF);
SPI.transfer(0);
for (int i = 0; i < 256; i++)
{
page_buffer[i] = SPI.transfer(0);
output[i] = page_buffer[i];
}
if(command.startsWith("read"))
{
printPage(page_buffer);
}
digitalWrite(CHIPSELECT, HIGH);
toggleCS();
}
void writeData(word page, byte offset, byte databyte)
{
char buff[80];
sprintf(buff, "Command: Write(%04xh, %04xh, %02xh)", page, offset, databyte);
Serial.println(buff);
byte page_data[256];
getPage(page, page_data);
page_data[offset] = databyte;
//Serial.println("Contents of page_data array before writePage command");
//printPage(page_data);
writePage(page, page_data);
Serial.println("Ready");
}
void writePage(word page_number, byte *page_buffer)
{
//Serial.println("Contents of page_data array passed to writePage command");
//printPage(page_buffer);
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(WREN);
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(PAGE_PROG);
SPI.transfer((page_number >> 8) & 0xFF);
SPI.transfer((page_number >> 0) & 0xFF);
SPI.transfer(0);
for (int i = 0; i < 256; i++)
{
SPI.transfer(page_buffer[i]);
}
digitalWrite(CHIPSELECT, HIGH);
toggleCS();
}
void toggleCS(void)
{
//Serial.println("toggleCS called");
digitalWrite(CHIPSELECT, HIGH);
digitalWrite(CHIPSELECT, LOW);
SPI.transfer(RDSR);
while(!SPI.transfer(0) & 1) { };
delay(1000);
digitalWrite(CHIPSELECT, HIGH);
}
void checkForSerial()
{
char c;
while (Serial.available()) {
c = (char)Serial.read();
//Serial.println(c);
if (c == ';') {
isReady = true;
}
else {
command += c;
}
}
if(!Serial.available())
{
//Serial.println("Serial is not available");
//delay(1000);
}
}
void setup() {
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK, OUTPUT);
pinMode(CHIPSELECT,OUTPUT);
SPI.begin();
SPI.beginTransaction(SPISettings(14000000, MSBFIRST,SPI_MODE0));
//SPI.setBitOrder(MSBFIRST);
Serial.begin(9600);
Serial.println("");
Serial.println("Ready");
//CPU_CCP = CCP_IOREG_gc; // enable CPU reset via command
//digitalWrite(CHIPSELECT, HIGH); // Disable Device
}
void loop() {
checkForSerial();
if (isReady)
{
if (command == "info") {
getInfo();
}
else if (command == "erase") {
erase();
}
else if (command == "testWREN") {
testWriteEnable();
}
// A one-parameter command...
else if (command.startsWith("read")) {
int pos = command.indexOf(" ");
if (pos == -1) {
Serial.println("Error: Command 'readPage' expects an int operand");
} else {
word page = (word)command.substring(pos).toInt();
readPage(page);
}
}
// A three-parameter command..
else if (command.startsWith("write")) {
word pageno;
byte offset;
byte data;
String args[3];
for (int i = 0; i < 3; ++i) {
int pos = command.indexOf(" ");
if (pos == -1) {
Serial.println("Syntax error in writeByte");
goto done;
}
args[i] = command.substring(pos + 1);
command = args[i];
}
pageno = (word)args[0].toInt();
offset = (byte)args[1].toInt();
data = (byte)args[2].toInt();
writeData(pageno, offset, data);
}
else {
Serial.print("Invalid command sent: ");
Serial.println(command);
}
done:
command = "";
isReady = false;
}
//else { Serial.print("Not ready"); }
}
Where am I going wrong? Is it my code? or is it my wiring? Any help would be appreciated.
#define CLK 2
#define DT 3
#define SW 4
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
LiquidCrystal_PCF8574 lcd(0x27);
int counter = 0;
int currentStateCLK;
int lastStateCLK;
int lastCLK,cnt=0,btnState,lastPress=0;
String currentDir ="";
unsigned long lastButtonPress = 0;
char *mainmenu[] ={"SET MODE","SET TEMP","SET HUMD","SERVO","RESET"};
char *setmode[] ={"INCUBATOR","HATCHER","BACK"};
void setup() {
// Set encoder pins as inputs
Wire.begin();
Wire.beginTransmission(0x27);
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);
lcd.begin(16, 2);
lcd.setBacklight(255);
lcd.home(); lcd.clear();
Serial.begin(9600);
lastStateCLK = digitalRead(CLK);
delay(100);
if(EEPROM_READ(0)==NULL){
SET_MODE();
}
Serial.print(EEPROM_READ(0));
}
void loop(){
disp();
rot();
}
void disp(){
lcd.setCursor(0,0);
lcd.print(" KGF");
}
void rot() {
int lim=sizeof(mainmenu)/2;
Serial.print(lim);
currentStateCLK = digitalRead(CLK);
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
lcd.clear();
lcd.setCursor(0, 1);
if (digitalRead(DT) != currentStateCLK) {
counter --;
if(counter<0){
counter=lim-1;
}
}
else {
// Encoder is rotating CW so increment
counter ++;
if(counter>lim-1){
counter=0;
}
lcd.print(mainmenu[counter]);
}
lastStateCLK = currentStateCLK;
int btnState = digitalRead(SW);
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
if(counter==0){
SET_MODE();
}
}
}
lastButtonPress = millis();
}
delay(1);
}
void SET_MODE(){
int lim=sizeof(setmode)/2;
int currentCLK = digitalRead(CLK);
if (currentCLK != lastCLK && currentCLK == 1){
lcd.clear();
lcd.setCursor(0, 1);
if (digitalRead(DT) != currentCLK) {
cnt --;
if(cnt<0){
cnt=lim-1;
}
}
else {
// Encoder is rotating CW so increment
cnt ++;
if(cnt>lim-1){
cnt=0;
}
}
lcd.print(setmode[cnt]);
}
lastCLK = currentCLK;
btnState = digitalRead(SW);
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
if(setmode[cnt]=="BACK"){
exit(0);
}
lcd.clear();
lcd.setCursor(0, 1);
EEPROM_WRITE(0,setmode[cnt]);
lcd.print("DONE");
}
lastPress = millis();
}
delay(1);
}
void EEPROM_WRITE(int addrOffset, const String &strToWrite)
{
byte len = strToWrite.length();
EEPROM.write(addrOffset, len);
for (int i = 0; i < len; i++)
{
EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
}
}
String EEPROM_READ(int addrOffset)
{
int newStrLen = EEPROM.read(addrOffset);
char data[newStrLen + 1];
for (int i = 0; i < newStrLen; i++)
{
data[i] = EEPROM.read(addrOffset + 1 + i);
}
data[newStrLen] = '\0';
return String(data);
}
I want to call the SET_MODE() function in the loop from rot() function, I am building a menu based program so the SET MODE menu should redirect to the SET_MODE() function, and as I will be adding more menu and sub-menus how can I perform this task.
The SET_MODE() function doesn't work in loop I do not know why, it only works when I all it under void loop() directly.
I'm trying to program a class to control the MPU6050 with the Arduino Wire library but when I run the code in my Arduino mini it freezes after a few seconds.
There is the code of the library and a test sketch:
// Include Wire Library for I2C
#include <Wire.h>
enum MPU6050_filter {_256Hz, _188Hz, _98Hz, _42Hz, _20Hz, _10Hz, _5Hz};
enum MPU6050_gyro {_250dps, _500dps, _1000dps, _2000dps};
enum MPU6050_accel {_2g, _4g, _8g, _16Hz};
class MPU6050
{
public:
MPU6050 ();
bool start (bool AD0_value);
void goToSleep ();
void stopSleeping ();
void setFilterVal (MPU6050_filter filter_val);
void setGyroRange (MPU6050_gyro range);
void setAccelRange (MPU6050_accel range);
bool dataAvailable ();
void getLastGyroData (float& gx, float& gy, float& gz);
void getRawGyroData (int& gx, int& gy, int& gz);
private:
void writeRegister (byte address, byte data);
byte readRegister (byte address);
void readData (byte start_address, byte bytes, byte* data);
float convertGyroToDPS (int gyro);
bool AD0_val;
MPU6050_filter filter;
MPU6050_accel accel_range;
MPU6050_gyro gyro_range;
unsigned long last_read;
const unsigned long min_read_time = 1;
};
MPU6050::MPU6050 () : AD0_val(false),
filter(_256Hz),
accel_range(_2g),
gyro_range(_250dps) {}
bool MPU6050::start (bool AD0_value)
{
AD0_val = AD0_value;
// init sample rate div to 0 (max sample rate)
writeRegister(0x19, 0);
// activate FIFO for gyroscope data
writeRegister(0x23, 0x70);
// clear config setup register
writeRegister(0x6B, 0);
// setup the register
writeRegister(0x37, 0x10);
// set interrupt by data ready
writeRegister(0x38, 0x01);
}
void MPU6050::goToSleep ()
{
byte prev_data = readRegister(0x6B);
prev_data = (prev_data | 0x40);
writeRegister(0x6B, prev_data);
}
void MPU6050::stopSleeping ()
{
byte prev_data = readRegister(0x6B);
prev_data = (prev_data & 0xBF);
writeRegister(0x6B, prev_data);
}
void MPU6050::setFilterVal (MPU6050_filter filter_val)
{
int val;
if (filter_val == _256Hz) val = 0;
else if (filter_val == _188Hz) val = 1;
else if (filter_val == _98Hz) val = 2;
else if (filter_val == _42Hz) val = 3;
else if (filter_val == _20Hz) val = 4;
else if (filter_val == _10Hz) val = 5;
else val = 6;
byte data = readRegister(0x1A);
data = (data & 0xF8) | (val & 0x07);
writeRegister(0x1A, data);
filter = filter_val;
}
void MPU6050::setAccelRange (MPU6050_accel range)
{
byte value;
if (range == _2g) value = 0;
else if (range == _4g) value = 1;
else if (range == _8g) value = 2;
else value = 3;
byte reg_value = readRegister(0x1C);
reg_value = (reg_value & 0xE0) | (value << 3);
writeRegister(0x1C, reg_value);
accel_range = range;
}
void MPU6050::setGyroRange (MPU6050_gyro range)
{
byte value;
if (range == _250dps) value = 0;
else if (range == _500dps) value = 1;
else if (range == _1000dps) value = 2;
else value = 3;
byte reg_value = readRegister(0x1B);
reg_value = (reg_value & 0xE0) | (value << 3);
writeRegister(0x1B, reg_value);
gyro_range = range;
}
bool MPU6050::dataAvailable ()
{
return (readRegister(0x3A) & 0x01);
}
void MPU6050::getLastGyroData (float& gx, float& gy, float& gz)
{
int raw_x, raw_y, raw_z;
getRawGyroData(raw_x, raw_y, raw_z);
gx = convertGyroToDPS(raw_x);
gy = convertGyroToDPS(raw_y);
gz = convertGyroToDPS(raw_z);
}
void MPU6050::getRawGyroData (int& gx, int& gy, int& gz)
{
byte* data = new byte[6];
readData(0x43, 6, data);
gx = data[0] << 8 | data[1];
gy = data[2] << 8 | data[3];
gz = data[4] << 8 | data[5];
delete data;
}
void MPU6050::writeRegister (byte address, byte data)
{
Wire.beginTransmission(0x68 + AD0_val);
Wire.write(address);
Wire.write(data);
Wire.endTransmission();
}
byte MPU6050::readRegister (byte address)
{
byte data_buff = 0x00;
Wire.beginTransmission(byte(0x68 + AD0_val));
//Send the requested starting register
Wire.write(address);
//End the transmission
Wire.endTransmission(false);
//Request 14 bytes from the MPU-6050
Wire.requestFrom(byte(0x68 + AD0_val), byte(0x01), byte(true));
unsigned long initial_time = millis();
//Wait until all the bytes are received
while(Wire.available() == 0 and millis() < initial_time + 5);
if (millis() < initial_time + 5)
{
// read the data
data_buff = Wire.read();
}
// end the transmission
Wire.endTransmission();
return data_buff;
}
void MPU6050::readData (byte start_address, byte bytes, byte* data)
{
Wire.beginTransmission(byte(0x68 + AD0_val));
//Send the requested starting register
Wire.write(start_address);
//End the transmission
Wire.endTransmission(false);
//Request 14 bytes from the MPU-6050
Wire.requestFrom(byte(0x68 + AD0_val), bytes, byte(true));
//Wait until all the bytes are received
while(Wire.available() < bytes);
for (int i = 0; i < bytes; i++)
data[i] = Wire.read();
Wire.endTransmission();
}
float MPU6050::convertGyroToDPS (int gyro)
{
if (gyro_range == _250dps) return float(gyro)/131.0;
else if (gyro_range == _500dps) return float(gyro)/65.5;
else if (gyro_range == _1000dps) return float(gyro)/32.8;
else return float(gyro)/16.4;
}
#define SHOW_EACH 50
MPU6050 chip;
unsigned long last_shown = 0;
unsigned this_fps = 0;
unsigned last_fps = 0;
unsigned last_time = 0;
unsigned total_fps = 0;
float g_x, g_y, g_z;
void setup()
{
Serial.begin(115200);
Serial.println("--------");
chip.setFilterVal(_256Hz);
chip.setGyroRange(_250dps);
chip.start(false);
}
void loop()
{
if (chip.dataAvailable())
chip.getLastGyroData(g_x, g_y, g_z);
++this_fps;
++total_fps;
if (millis()/1000 != last_time)
{
last_time = millis()/1000;
last_fps = this_fps;
this_fps = 0;
}
if (millis() - last_shown >= SHOW_EACH)
{
last_shown = millis();
Serial.print(g_x);
Serial.print(" ");
Serial.print(g_y);
Serial.print(" ");
Serial.print(g_y);
Serial.print(" ");
Serial.print(last_fps);
Serial.print(" ");
Serial.println(total_fps);
}
}
Some testing with Serial.println points to the function requestFrom from the Wire library. What can be the cause?
Sorry that i write this as an answer, but I can't write comments yet.
1st. There are multiple requestFrom() calls in your code, so it would be better to specify where does the problem occure exactly (if you can).
2nd. Are you completely sure that, it's the requestFrom() where your code hang. In readData() there is a while() just after requestFrom(). Maybe it hangs there, as the other device don't send enough bytes (for some reasons).
Anyway this might help a litle (link), here they recommend to always check the return value of endTransmission().
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