diff --git a/examples/Advanced/I2C_Tester/I2C_Tester.ino b/examples/Advanced/I2C_Tester/I2C_Tester.ino new file mode 100644 index 0000000..ab77f54 --- /dev/null +++ b/examples/Advanced/I2C_Tester/I2C_Tester.ino @@ -0,0 +1,53 @@ +#include + +void setup() +{ + M5.Lcd.begin(); + M5.Lcd.setRotation(3); + M5.Lcd.fillScreen( BLACK ); + M5.Lcd.setCursor(0, 0); + M5.Lcd.setTextColor(YELLOW); + M5.Lcd.setTextSize(1); + + M5.Lcd.fillScreen( BLACK ); + M5.Lcd.setCursor(0, 0); + M5.Lcd.println("M5StickC I2C Tester"); + + //For HY2.0-4P + Wire.begin(); + //For HAT + Wire1.begin(0,26); + + delay(3000); + M5.Lcd.fillScreen( BLACK ); +} + +int textColor=YELLOW; + +void loop() +{ + int address; + int error; + int error1; + M5.Lcd.setCursor(0, 0); + M5.Lcd.println("scanning Address [HEX]"); + + for(address = 1; address < 127; address++ ) + { + Wire.beginTransmission(address); + Wire1.beginTransmission(address); + error = Wire.endTransmission(); + error1 = Wire1.endTransmission(); + if((error==0)||(error1==0)) + { + M5.Lcd.print(address,HEX);M5.Lcd.print(" "); + } + else M5.Lcd.print("."); + + delay(10); + } + + if(textColor==YELLOW) textColor=GREEN; + else textColor=YELLOW; + M5.Lcd.setTextColor(textColor,BLACK); +} diff --git a/examples/Advanced/QRCode/QRCode.ino b/examples/Advanced/QRCode/QRCode.ino new file mode 100644 index 0000000..33e1c0f --- /dev/null +++ b/examples/Advanced/QRCode/QRCode.ino @@ -0,0 +1,12 @@ +#include + +void setup() { + M5.begin(); + + // Display QRCode + M5.Lcd.qrcode("http://www.m5stack.com", 0, 0, 135); + // M5.Lcd.qrcode(const char *string, uint16_t x = 50, uint16_t y = 10, uint8_t width = 220, uint8_t version = 6); +} + +void loop() { +} diff --git a/examples/Basics/AXP192/AXP192.ino b/examples/Basics/AXP192/AXP192.ino new file mode 100644 index 0000000..e188120 --- /dev/null +++ b/examples/Basics/AXP192/AXP192.ino @@ -0,0 +1,49 @@ +#include +#include "AXP192.h" +TFT_eSprite tftSprite = TFT_eSprite(&M5.Lcd); + +void setup() { + M5.begin(); + M5.Lcd.setRotation(3); + tftSprite.createSprite(160, 80); + tftSprite.setRotation(3); + M5.Axp.EnableCoulombcounter(); +} + +void loop() { + tftSprite.fillSprite(BLACK); + tftSprite.setCursor(0, 0, 1); + tftSprite.printf("AXP Temp: %.1fC \r\n", M5.Axp.GetTempInAXP192()); + tftSprite.setCursor(0, 10); + tftSprite.printf("Bat:\r\n V: %.3fv I: %.3fma\r\n", M5.Axp.GetBatVoltage(), M5.Axp.GetBatCurrent()); + tftSprite.setCursor(0, 30); + tftSprite.printf("USB:\r\n V: %.3fv I: %.3fma\r\n", M5.Axp.GetVBusVoltage(), M5.Axp.GetVBusCurrent()); + tftSprite.setCursor(0, 50); + tftSprite.printf("5V-In:\r\n V: %.3fv I: %.3fma\r\n", M5.Axp.GetVinVoltage(), M5.Axp.GetVinCurrent()); + tftSprite.setCursor(0, 70); + tftSprite.printf("Bat power %.3fmw", M5.Axp.GetBatPower()); + tftSprite.pushSprite(40, 20); + + // 0x01 long press(1s), 0x02 press + if(M5.Axp.GetBtnPress() == 0x02) + { + esp_restart(); + } + + if(M5.BtnA.wasPressed()) + { + // close tft voltage output + M5.Axp.SetLDO2(false); + } + + if(M5.BtnB.wasPressed()) + { + // close tft voltage output + M5.Axp.SetLDO2(true); + } + + // M5.Axp.SetChargeCurrent(CURRENT_100MA); + + M5.update(); + delay(100); +} diff --git a/examples/Basics/Display/Display.ino b/examples/Basics/Display/Display.ino new file mode 100644 index 0000000..7a50e01 --- /dev/null +++ b/examples/Basics/Display/Display.ino @@ -0,0 +1,47 @@ +#include + +// the setup routine runs once when M5StickC starts up +void setup() { + + // initialize the M5StickC object + M5.begin(); + + // Lcd display + M5.Lcd.fillScreen(WHITE); + delay(500); + M5.Lcd.fillScreen(RED); + delay(500); + M5.Lcd.fillScreen(GREEN); + delay(500); + M5.Lcd.fillScreen(BLUE); + delay(500); + M5.Lcd.fillScreen(BLACK); + delay(500); + + // text print + M5.Lcd.fillScreen(BLACK); + M5.Lcd.setCursor(0, 10); + M5.Lcd.setTextColor(WHITE); + M5.Lcd.setTextSize(1); + M5.Lcd.printf("Display Test!"); + + // draw graphic + delay(1000); + M5.Lcd.drawRect(15, 55, 50, 50, BLUE); + delay(1000); + M5.Lcd.fillRect(15, 55, 50, 50, BLUE); + delay(1000); + M5.Lcd.drawCircle(40, 80, 30, RED); + delay(1000); + M5.Lcd.fillCircle(40, 80, 30, RED); + delay(1000); +} + +// the loop routine runs over and over again forever +void loop(){ + + //rand draw + M5.Lcd.fillTriangle(random(M5.Lcd.width()-1), random(M5.Lcd.height()-1), random(M5.Lcd.width()-1), random(M5.Lcd.height()-1), random(M5.Lcd.width()-1), random(M5.Lcd.height()-1), random(0xfffe)); + + //M5.update(); +} diff --git a/examples/Basics/IMU/IMU.ino b/examples/Basics/IMU/IMU.ino new file mode 100644 index 0000000..f953e03 --- /dev/null +++ b/examples/Basics/IMU/IMU.ino @@ -0,0 +1,58 @@ +#include + +float accX = 0.0F; +float accY = 0.0F; +float accZ = 0.0F; + +float gyroX = 0.0F; +float gyroY = 0.0F; +float gyroZ = 0.0F; + +float pitch = 0.0F; +float roll = 0.0F; +float yaw = 0.0F; + +void setup() { + // put your setup code here, to run once: + M5.begin(); + M5.IMU.Init(); + M5.Lcd.setRotation(3); + M5.Lcd.fillScreen(BLACK); + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor(80, 15); + M5.Lcd.println("IMU TEST"); + M5.Lcd.setCursor(30, 30); + M5.Lcd.println(" X Y Z"); + M5.Lcd.setCursor(30, 70); + M5.Lcd.println(" Pitch Roll Yaw"); +} + +float temp = 0; +/***************************************** +M5.IMU.getGyroData(&gyroX,&gyroY,&gyroZ); +M5.IMU.getAccelData(&accX,&accY,&accZ); +M5.IMU.getAhrsData(&pitch,&roll,&yaw); +M5.IMU.getTempData(&temp); +*****************************************/ +void loop() { + // put your main code here, to run repeatedly: + M5.IMU.getGyroData(&gyroX,&gyroY,&gyroZ); + M5.IMU.getAccelData(&accX,&accY,&accZ); + M5.IMU.getAhrsData(&pitch,&roll,&yaw); + M5.IMU.getTempData(&temp); + + M5.Lcd.setCursor(30, 40); + M5.Lcd.printf("%6.2f %6.2f %6.2f ", gyroX, gyroY, gyroZ); + M5.Lcd.setCursor(170, 40); + M5.Lcd.print("o/s"); + M5.Lcd.setCursor(30, 50); + M5.Lcd.printf(" %5.2f %5.2f %5.2f ", accX, accY, accZ); + M5.Lcd.setCursor(170, 50); + M5.Lcd.print("G"); + M5.Lcd.setCursor(30, 80); + M5.Lcd.printf(" %5.2f %5.2f %5.2f ", pitch, roll, yaw); + + M5.Lcd.setCursor(30, 95); + M5.Lcd.printf("Temperature : %.2f C", temp); + delay(100); +} diff --git a/examples/Basics/MPU6886/MPU6886.ino b/examples/Basics/MPU6886/MPU6886.ino new file mode 100644 index 0000000..2648a7c --- /dev/null +++ b/examples/Basics/MPU6886/MPU6886.ino @@ -0,0 +1,42 @@ +#include + +float accX = 0; +float accY = 0; +float accZ = 0; + +float gyroX = 0; +float gyroY = 0; +float gyroZ = 0; + +float temp = 0; +void setup() { + // put your setup code here, to run once: + M5.begin(); + M5.Lcd.setRotation(3); + M5.Lcd.fillScreen(BLACK); + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor(50, 15); + M5.Lcd.println("MPU6886 TEST"); + M5.Lcd.setCursor(30, 30); + M5.Lcd.println(" X Y Z"); + M5.Imu.Init(); +} + +void loop() { + // put your main code here, to run repeatedly: + M5.Imu.getGyroData(&gyroX,&gyroY,&gyroZ); + M5.Imu.getAccelData(&accX,&accY,&accZ); + M5.Imu.getTempData(&temp); + + M5.Lcd.setCursor(30, 45); + M5.Lcd.printf("%.2f %.2f %.2f ", gyroX, gyroY,gyroZ); + M5.Lcd.setCursor(170, 45); + M5.Lcd.print("o/s"); + M5.Lcd.setCursor(30, 60); + M5.Lcd.printf("%.2f %.2f %.2f ",accX * 1000,accY * 1000, accZ * 1000); + M5.Lcd.setCursor(185, 60); + M5.Lcd.print("mg"); + M5.Lcd.setCursor(30, 75); + M5.Lcd.printf("Temperature : %.2f C", temp); + delay(100); +} diff --git a/examples/Basics/Micophone/Micophone.ino b/examples/Basics/Micophone/Micophone.ino new file mode 100644 index 0000000..2bd564e --- /dev/null +++ b/examples/Basics/Micophone/Micophone.ino @@ -0,0 +1,79 @@ +#include +#include + +#define PIN_CLK 0 +#define PIN_DATA 34 +#define READ_LEN (2 * 256) +#define GAIN_FACTOR 3 +uint8_t BUFFER[READ_LEN] = {0}; + +uint16_t oldy[160]; +int16_t *adcBuffer = NULL; + +void showSignal(){ + int y; + for (int n = 0; n < 160; n++){ + y = adcBuffer[n] * GAIN_FACTOR; + y = map(y, INT16_MIN, INT16_MAX, 10, 70); + M5.Lcd.drawPixel(n, oldy[n],WHITE); + M5.Lcd.drawPixel(n,y,BLACK); + oldy[n] = y; + } +} + + +void mic_record_task (void* arg) +{ + size_t bytesread; + while(1){ + i2s_read(I2S_NUM_0,(char*) BUFFER, READ_LEN, &bytesread, (100 / portTICK_RATE_MS)); + adcBuffer = (int16_t *)BUFFER; + showSignal(); + vTaskDelay(100 / portTICK_RATE_MS); + } +} + +void i2sInit() +{ + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM), + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB + .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT, + .communication_format = I2S_COMM_FORMAT_I2S, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 2, + .dma_buf_len = 128, + }; + + i2s_pin_config_t pin_config; + pin_config.bck_io_num = I2S_PIN_NO_CHANGE; + pin_config.ws_io_num = PIN_CLK; + pin_config.data_out_num = I2S_PIN_NO_CHANGE; + pin_config.data_in_num = PIN_DATA; + + + i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM_0, &pin_config); + i2s_set_clk(I2S_NUM_0, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); +} + + +void setup() { + M5.begin(); + M5.Lcd.setRotation(3); + M5.Lcd.fillScreen(WHITE); + M5.Lcd.setTextColor(BLACK, WHITE); + M5.Lcd.println("mic test"); + + i2sInit(); + xTaskCreate(mic_record_task, "mic_record_task", 2048, NULL, 1, NULL); +} + + + + +void loop() { + printf("loop cycling\n"); + vTaskDelay(1000 / portTICK_RATE_MS); // otherwise the main task wastes half of the cpu cycles +} diff --git a/examples/Basics/RTC/RTC.ino b/examples/Basics/RTC/RTC.ino new file mode 100644 index 0000000..32eaa18 --- /dev/null +++ b/examples/Basics/RTC/RTC.ino @@ -0,0 +1,39 @@ +#include + +RTC_TimeTypeDef RTC_TimeStruct; +RTC_DateTypeDef RTC_DateStruct; + +void setup() { + // put your setup code here, to run once: + M5.begin(); + M5.Lcd.setRotation(3); + M5.Lcd.fillScreen(BLACK); + + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor(60, 10, 2); + M5.Lcd.println("RTC TEST"); + RTC_TimeTypeDef TimeStruct; + TimeStruct.Hours = 18; + TimeStruct.Minutes = 56; + TimeStruct.Seconds = 10; + M5.Rtc.SetTime(&TimeStruct); + RTC_DateTypeDef DateStruct; + DateStruct.WeekDay = 3; + DateStruct.Month = 3; + DateStruct.Date = 22; + DateStruct.Year = 2019; + M5.Rtc.SetData(&DateStruct); +} + +void loop() { + // put your main code here, to run repeatedly: + M5.Rtc.GetTime(&RTC_TimeStruct); + M5.Rtc.GetData(&RTC_DateStruct); + M5.Lcd.setCursor(30, 35); + M5.Lcd.printf("Data: %04d-%02d-%02d\n",RTC_DateStruct.Year, RTC_DateStruct.Month,RTC_DateStruct.Date); + M5.Lcd.setCursor(30, 55); + M5.Lcd.printf("Week: %d\n",RTC_DateStruct.WeekDay); + M5.Lcd.setCursor(30, 75); + M5.Lcd.printf("Time: %02d : %02d : %02d\n",RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds); + delay(500); +} diff --git a/example/FactoryTest/FactoryTest.ino b/examples/FactoryTest/FactoryTest.ino similarity index 100% rename from example/FactoryTest/FactoryTest.ino rename to examples/FactoryTest/FactoryTest.ino diff --git a/example/FactoryTest/Icon.c b/examples/FactoryTest/Icon.c similarity index 100% rename from example/FactoryTest/Icon.c rename to examples/FactoryTest/Icon.c diff --git a/example/FactoryTest/TFTTerminal.cpp b/examples/FactoryTest/TFTTerminal.cpp similarity index 100% rename from example/FactoryTest/TFTTerminal.cpp rename to examples/FactoryTest/TFTTerminal.cpp diff --git a/example/FactoryTest/TFTTerminal.h b/examples/FactoryTest/TFTTerminal.h similarity index 100% rename from example/FactoryTest/TFTTerminal.h rename to examples/FactoryTest/TFTTerminal.h diff --git a/example/FactoryTest/fft.cpp b/examples/FactoryTest/fft.cpp similarity index 100% rename from example/FactoryTest/fft.cpp rename to examples/FactoryTest/fft.cpp diff --git a/example/FactoryTest/fft.h b/examples/FactoryTest/fft.h similarity index 100% rename from example/FactoryTest/fft.h rename to examples/FactoryTest/fft.h diff --git a/examples/Games/Dices/Dices.ino b/examples/Games/Dices/Dices.ino new file mode 100644 index 0000000..2fb7fe0 --- /dev/null +++ b/examples/Games/Dices/Dices.ino @@ -0,0 +1,80 @@ +/* + +A couple of dices on a tiny 80x160px TFT display + +Author: Alfonso de Cala + +*/ + +#include + +#define DOT_SIZE 6 +int dot[6][6][2] { + {{35,35}}, + {{15,15},{55,55}}, + {{15,15},{35,35},{55,55}}, + {{15,15},{15,55},{55,15},{55,55}}, + {{15,15},{15,55},{35,35},{55,15},{55,55}}, + {{15,15},{15,35},{15,55},{55,15},{55,35},{55,55}}, + }; + +float accX = 0; +float accY = 0; +float accZ = 0; + + +void setup(void) { + M5.begin(); + M5.IMU.Init(); + + M5.Lcd.setRotation(1); + + M5.Lcd.fillScreen(TFT_GREEN); + + M5.Lcd.setTextColor(TFT_BLACK); // Adding a background colour erases previous text automatically + + + M5.Lcd.setCursor(10, 30); + M5.Lcd.setTextSize(3); + M5.Lcd.print("SHAKE ME"); + delay(1000); +} + + +void draw_dice(int16_t x, int16_t y, int n) { + + M5.Lcd.fillRect(x, y, 70, 70, WHITE); + + for(int d = 0; d < 6; d++) { + if (dot[n][d][0] > 0) { + M5.Lcd.fillCircle(x+dot[n][d][0], y+dot[n][d][1], DOT_SIZE, TFT_BLACK); + } + } + +} + + +void loop() { + + + while(1) { + M5.IMU.getAccelData(&accX,&accY,&accZ); + if (accX > 1.5 || accY > 1.5 ) { + break; + } + } + + M5.Lcd.fillScreen(TFT_GREEN); + + // Draw first dice + delay(500); // A little delay to increase suspense :-D + int number = random(0, 6); + draw_dice(50,30,number); + + // Draw second dice + delay(500); + number = random(0, 6); + draw_dice(125,30,number); + + +} diff --git a/examples/Games/FlappyBird/FlappyBird.ino b/examples/Games/FlappyBird/FlappyBird.ino new file mode 100644 index 0000000..0b8e93c --- /dev/null +++ b/examples/Games/FlappyBird/FlappyBird.ino @@ -0,0 +1,370 @@ +// By Ponticelli Domenico. +// 12NOV2020 EEPROM Working now, Modified by Zontex +// https://github.com/pcelli85/M5Stack_FlappyBird_game + +#include +#include + +#define TFTW 135 // screen width +#define TFTH 240 // screen height +#define TFTW2 67 // half screen width +#define TFTH2 120 // half screen height +// game constant +#define SPEED 1 +#define GRAVITY 9.8 +#define JUMP_FORCE 2.15 +#define SKIP_TICKS 20.0 // 1000 / 50fps +#define MAX_FRAMESKIP 5 +// bird size +#define BIRDW 8 // bird width +#define BIRDH 8 // bird height +#define BIRDW2 4 // half width +#define BIRDH2 4 // half height +// pipe size +#define PIPEW 15 // pipe width +#define GAPHEIGHT 30 // pipe gap height +// floor size +#define FLOORH 20 // floor height (from bottom of the screen) +// grass size +#define GRASSH 4 // grass height (inside floor, starts at floor y) + +int address = 0; +int maxScore = EEPROM.readInt(address); +const int buttonPin = 2; +// background +const unsigned int BCKGRDCOL = M5.Lcd.color565(138,235,244); +// bird +const unsigned int BIRDCOL = M5.Lcd.color565(255,254,174); +// pipe +const unsigned int PIPECOL = M5.Lcd.color565(99,255,78); +// pipe highlight +const unsigned int PIPEHIGHCOL = M5.Lcd.color565(250,255,250); +// pipe seam +const unsigned int PIPESEAMCOL = M5.Lcd.color565(0,0,0); +// floor +const unsigned int FLOORCOL = M5.Lcd.color565(246,240,163); +// grass (col2 is the stripe color) +const unsigned int GRASSCOL = M5.Lcd.color565(141,225,87); +const unsigned int GRASSCOL2 = M5.Lcd.color565(156,239,88); + +// bird sprite +// bird sprite colors (Cx name for values to keep the array readable) +#define C0 BCKGRDCOL +#define C1 M5.Lcd.color565(195,165,75) +#define C2 BIRDCOL +#define C3 TFT_WHITE +#define C4 TFT_RED +#define C5 M5.Lcd.color565(251,216,114) + +static unsigned int birdcol[] = +{ C0, C0, C1, C1, C1, C1, C1, C0, C0, C0, C1, C1, C1, C1, C1, C0, + C0, C1, C2, C2, C2, C1, C3, C1, C0, C1, C2, C2, C2, C1, C3, C1, + C0, C2, C2, C2, C2, C1, C3, C1, C0, C2, C2, C2, C2, C1, C3, C1, + C1, C1, C1, C2, C2, C3, C1, C1, C1, C1, C1, C2, C2, C3, C1, C1, + C1, C2, C2, C2, C2, C2, C4, C4, C1, C2, C2, C2, C2, C2, C4, C4, + C1, C2, C2, C2, C1, C5, C4, C0, C1, C2, C2, C2, C1, C5, C4, C0, + C0, C1, C2, C1, C5, C5, C5, C0, C0, C1, C2, C1, C5, C5, C5, C0, + C0, C0, C1, C5, C5, C5, C0, C0, C0, C0, C1, C5, C5, C5, C0, C0}; + +// bird structure +static struct BIRD { + long x, y, old_y; + long col; + float vel_y; +} bird; + +// pipe structure +static struct PIPES { + long x, gap_y; + long col; +} pipes; + +// score +int score; +// temporary x and y var +static short tmpx, tmpy; + +// --------------- +// draw pixel +// --------------- +// faster drawPixel method by inlining calls and using setAddrWindow and pushColor +// using macro to force inlining +#define drawPixel(a, b, c) M5.Lcd.setAddrWindow(a, b, a, b); M5.Lcd.pushColor(c) +// --------------- +// game loop +// --------------- +void game_loop() { + // =============== + // prepare game variables + // draw floor + // =============== + // instead of calculating the distance of the floor from the screen height each time store it in a variable + unsigned char GAMEH = TFTH - FLOORH; + // draw the floor once, we will not overwrite on this area in-game + // black line + M5.Lcd.drawFastHLine(0, GAMEH, TFTW, TFT_BLACK); + // grass and stripe + M5.Lcd.fillRect(0, GAMEH+1, TFTW2, GRASSH, GRASSCOL); + M5.Lcd.fillRect(TFTW2, GAMEH+1, TFTW2, GRASSH, GRASSCOL2); + // black line + M5.Lcd.drawFastHLine(0, GAMEH+GRASSH, TFTW, TFT_BLACK); + // mud + M5.Lcd.fillRect(0, GAMEH+GRASSH+1, TFTW, FLOORH-GRASSH, FLOORCOL); + // grass x position (for stripe animation) + long grassx = TFTW; + // game loop time variables + double delta, old_time, next_game_tick, current_time; + next_game_tick = current_time = millis(); + int loops; + // passed pipe flag to count score + bool passed_pipe = false; + // temp var for setAddrWindow + unsigned char px; + + while (1) { + loops = 0; + while( millis() > next_game_tick && loops < MAX_FRAMESKIP) { + if(digitalRead(M5_BUTTON_HOME) == LOW){ + //while(digitalRead(M5_BUTTON_HOME) == LOW); + if (bird.y > BIRDH2*0.5) bird.vel_y = -JUMP_FORCE; + // else zero velocity + else bird.vel_y = 0; + } + + // =============== + // update + // =============== + // calculate delta time + // --------------- + old_time = current_time; + current_time = millis(); + delta = (current_time-old_time)/1000; + + // bird + // --------------- + bird.vel_y += GRAVITY * delta; + bird.y += bird.vel_y; + + // pipe + // --------------- + + pipes.x -= SPEED; + // if pipe reached edge of the screen reset its position and gap + if (pipes.x < -PIPEW) { + pipes.x = TFTW; + pipes.gap_y = random(10, GAMEH-(10+GAPHEIGHT)); + } + + // --------------- + next_game_tick += SKIP_TICKS; + loops++; + } + + // =============== + // draw + // =============== + // pipe + // --------------- + // we save cycles if we avoid drawing the pipe when outside the screen + + if (pipes.x >= 0 && pipes.x < TFTW) { + // pipe color + M5.Lcd.drawFastVLine(pipes.x+3, 0, pipes.gap_y, PIPECOL); + M5.Lcd.drawFastVLine(pipes.x+3, pipes.gap_y+GAPHEIGHT+1, GAMEH-(pipes.gap_y+GAPHEIGHT+1), PIPECOL); + // highlight + M5.Lcd.drawFastVLine(pipes.x, 0, pipes.gap_y, PIPEHIGHCOL); + M5.Lcd.drawFastVLine(pipes.x, pipes.gap_y+GAPHEIGHT+1, GAMEH-(pipes.gap_y+GAPHEIGHT+1), PIPEHIGHCOL); + // bottom and top border of pipe + drawPixel(pipes.x, pipes.gap_y, PIPESEAMCOL); + drawPixel(pipes.x, pipes.gap_y+GAPHEIGHT, PIPESEAMCOL); + // pipe seam + drawPixel(pipes.x, pipes.gap_y-6, PIPESEAMCOL); + drawPixel(pipes.x, pipes.gap_y+GAPHEIGHT+6, PIPESEAMCOL); + drawPixel(pipes.x+3, pipes.gap_y-6, PIPESEAMCOL); + drawPixel(pipes.x+3, pipes.gap_y+GAPHEIGHT+6, PIPESEAMCOL); + } +#if 1 + // erase behind pipe + if (pipes.x <= TFTW) + M5.Lcd.drawFastVLine(pipes.x+PIPEW, 0, GAMEH, BCKGRDCOL); + //M5.Lcd.drawFastVLine(pipes.x, 0, GAMEH, BCKGRDCOL); + // PIPECOL +#endif + // bird + // --------------- + tmpx = BIRDW-1; + do { + px = bird.x+tmpx+BIRDW; + // clear bird at previous position stored in old_y + // we can't just erase the pixels before and after current position + // because of the non-linear bird movement (it would leave 'dirty' pixels) + tmpy = BIRDH - 1; + do { + drawPixel(px, bird.old_y + tmpy, BCKGRDCOL); + } while (tmpy--); + // draw bird sprite at new position + tmpy = BIRDH - 1; + do { + drawPixel(px, bird.y + tmpy, birdcol[tmpx + (tmpy * BIRDW)]); + } while (tmpy--); + } while (tmpx--); + // save position to erase bird on next draw + bird.old_y = bird.y; + + // grass stripes + // --------------- + grassx -= SPEED; + if (grassx < 0) grassx = TFTW; + M5.Lcd.drawFastVLine( grassx %TFTW, GAMEH+1, GRASSH-1, GRASSCOL); + M5.Lcd.drawFastVLine((grassx+64)%TFTW, GAMEH+1, GRASSH-1, GRASSCOL2); + + // =============== + // collision + // =============== + // if the bird hit the ground game over + if (bird.y > GAMEH-BIRDH) break; + // checking for bird collision with pipe + if (bird.x+BIRDW >= pipes.x-BIRDW2 && bird.x <= pipes.x+PIPEW-BIRDW) { + // bird entered a pipe, check for collision + if (bird.y < pipes.gap_y || bird.y+BIRDH > pipes.gap_y+GAPHEIGHT) break; + else passed_pipe = true; + } + // if bird has passed the pipe increase score + else if (bird.x > pipes.x+PIPEW-BIRDW && passed_pipe) { + passed_pipe = false; + // erase score with background color + M5.Lcd.setTextColor(BCKGRDCOL); + M5.Lcd.setCursor( TFTW2, 4); + M5.Lcd.print(score); + // set text color back to white for new score + M5.Lcd.setTextColor(TFT_WHITE); + // increase score since we successfully passed a pipe + score++; + } + + // update score + // --------------- + M5.Lcd.setCursor( 2, 4); + M5.Lcd.print(score); + } + + // add a small delay to show how the player lost + delay(1200); +} + +void game_init() { + // clear screen + M5.Lcd.fillScreen(BCKGRDCOL); + // reset score + score = 0; + // init bird + bird.x = 30; + bird.y = bird.old_y = TFTH2 - BIRDH; + bird.vel_y = -JUMP_FORCE; + tmpx = tmpy = 0; + // generate new random seed for the pipe gape + randomSeed(analogRead(0)); + // init pipe + pipes.x = 0; + pipes.gap_y = random(20, TFTH-60); +} + +// --------------- +// game start +// --------------- +void game_start() { + M5.Lcd.fillScreen(TFT_BLACK); + M5.Lcd.fillRect(0, TFTH2 - 10, TFTW, 1, TFT_WHITE); + M5.Lcd.fillRect(0, TFTH2 + 15, TFTW, 1, TFT_WHITE); + M5.Lcd.setTextColor(TFT_WHITE); + M5.Lcd.setTextSize(1); + // half width - num char * char width in pixels + M5.Lcd.setCursor( TFTW2-15, TFTH2 - 6); + M5.Lcd.println("FLAPPY"); + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor( TFTW2-15, TFTH2 + 6); + M5.Lcd.println("-BIRD-"); + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor( 15, TFTH2 - 21); + M5.Lcd.println("M5StickC"); + M5.Lcd.setCursor( TFTW2 - 40, TFTH2 + 21); + M5.Lcd.println("please press home"); + while (1) { + // wait for push button + if(digitalRead(M5_BUTTON_HOME) == LOW){ + while(digitalRead(M5_BUTTON_HOME) == LOW); + break; + } + + } + // init game settings + game_init(); +} + + + + +// --------------- +// game over +// --------------- +void game_over() { + M5.Lcd.fillScreen(TFT_BLACK); + maxScore = EEPROM.readInt(address); + + if(score>maxScore) + { + EEPROM.writeInt(address, score); + EEPROM.commit(); + maxScore = score; + M5.Lcd.setTextColor(TFT_RED); + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor( 0, TFTH2 - 16); + M5.Lcd.println("NEW HIGHSCORE"); + } + + M5.Lcd.setTextColor(TFT_WHITE); + M5.Lcd.setTextSize(1); + // half width - num char * char width in pixels + M5.Lcd.setCursor( TFTW2 - 25, TFTH2 - 6); + M5.Lcd.println("GAME OVER"); + M5.Lcd.setTextSize(1); + M5.Lcd.setCursor( 1, 10); + M5.Lcd.print("score: "); + M5.Lcd.print(score); + M5.Lcd.setCursor( 5, TFTH2 + 6); + M5.Lcd.println("press button"); + M5.Lcd.setCursor( 1, 21); + M5.Lcd.print("Max Score:"); + M5.Lcd.print(maxScore); + while(1) { + // wait for push button + if(digitalRead(M5_BUTTON_HOME) == LOW){ + while(digitalRead(M5_BUTTON_HOME) == LOW); + break; + } + } +} + +void resetMaxScore() +{ + EEPROM.writeInt(address, 0); + EEPROM.commit(); +} + +void setup() { + // put your setup code here, to run once: + M5.begin(); + EEPROM.begin(1000); + pinMode(M5_BUTTON_HOME, INPUT); + //resetMaxScore(); + Serial.println("last score:"); + Serial.println(EEPROM.readInt(address)); + +} + +void loop() { + // put your main code here, to run repeatedly: + game_start(); + game_loop(); + game_over(); +} diff --git a/library.properties b/library.properties index aaf62d7..d911bb8 100644 --- a/library.properties +++ b/library.properties @@ -2,7 +2,7 @@ name=M5StickCPlus version=0.0.1 author=M5StickCPlus maintainer=Hades -sentence=Library for M5StickC Core development kit +sentence=Library for M5StickC Plus development kit paragraph=See more on http://M5Stack.com category=Device Control url=https://github.com/m5stack/M5StickC-Plus.git diff --git a/src/utility/MPU6886.cpp b/src/utility/MPU6886.cpp index b7d76af..9ad7b6c 100644 --- a/src/utility/MPU6886.cpp +++ b/src/utility/MPU6886.cpp @@ -126,6 +126,25 @@ void MPU6886::getTempAdc(int16_t *t){ *t=((uint16_t)buf[0]<<8)|buf[1]; } +//!俯仰,航向,横滚:pitch,yaw,roll,指三维空间中飞行器的旋转状态。 +void MPU6886::getAhrsData(float *pitch,float *roll,float *yaw){ + + float accX = 0; + float accY = 0; + float accZ = 0; + + float gyroX = 0; + float gyroY = 0; + float gyroZ = 0; + + + getGyroData(&gyroX,&gyroY,&gyroZ); + getAccelData(&accX,&accY,&accZ); + + MahonyAHRSupdateIMU(gyroX * DEG_TO_RAD, gyroY * DEG_TO_RAD, gyroZ * DEG_TO_RAD, accX, accY, accZ,pitch,roll,yaw); + +} + void MPU6886::getGres(){ switch (Gyscale) diff --git a/src/utility/MPU6886.h b/src/utility/MPU6886.h index cb99c09..2709e85 100644 --- a/src/utility/MPU6886.h +++ b/src/utility/MPU6886.h @@ -10,6 +10,7 @@ #include #include +#include "MahonyAHRS.h" #define MPU6886_ADDRESS 0x68 #define MPU6886_WHOAMI 0x75 @@ -79,6 +80,8 @@ class MPU6886 { void SetGyroFsr(Gscale scale); void SetAccelFsr(Ascale scale); + void getAhrsData(float *pitch,float *roll,float *yaw); + public: float aRes, gRes; diff --git a/src/utility/MahonyAHRS.cpp b/src/utility/MahonyAHRS.cpp new file mode 100644 index 0000000..439d944 --- /dev/null +++ b/src/utility/MahonyAHRS.cpp @@ -0,0 +1,255 @@ +//===================================================================================================== +// MahonyAHRS.c +//===================================================================================================== +// +// Madgwick's implementation of Mayhony's AHRS algorithm. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== + +//--------------------------------------------------------------------------------------------------- +// Header files + +#include "MahonyAHRS.h" +#include +#include + +//--------------------------------------------------------------------------------------------------- +// Definitions + +#define sampleFreq 25.0f // sample frequency in Hz +#define twoKpDef (2.0f * 1.0f) // 2 * proportional gain +#define twoKiDef (2.0f * 0.0f) // 2 * integral gain + +//#define twoKiDef (0.0f * 0.0f) + +//--------------------------------------------------------------------------------------------------- +// Variable definitions + +volatile float twoKp = twoKpDef; // 2 * proportional gain (Kp) +volatile float twoKi = twoKiDef; // 2 * integral gain (Ki) +volatile float q0 = 1.0, q1 = 0.0, q2 = 0.0, q3 = 0.0; // quaternion of sensor frame relative to auxiliary frame +volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +//float invSqrt(float x); + +//==================================================================================================== +// Functions + +//--------------------------------------------------------------------------------------------------- +// AHRS algorithm update + +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { + float recipNorm; + float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float hx, hy, bx, bz; + float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + //MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az); + return; + } + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = sqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = sqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); + hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + bx = sqrt(hx * hx + hy * hy); + bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; + halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); + halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); + halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); + halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); + halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +//--------------------------------------------------------------------------------------------------- +// IMU algorithm update + +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float *pitch,float *roll,float *yaw) { + float recipNorm; + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; + + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = q1 * q3 - q0 * q2; + halfvy = q0 * q1 + q2 * q3; + halfvz = q0 * q0 - 0.5f + q3 * q3; + + + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + + + *pitch = asin(-2 * q1 * q3 + 2 * q0* q2); // pitch + *roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1); // roll + *yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3); //yaw + + *pitch *= RAD_TO_DEG; + *yaw *= RAD_TO_DEG; + // Declination of SparkFun Electronics (40°05'26.6"N 105°11'05.9"W) is + // 8° 30' E ± 0° 21' (or 8.5°) on 2016-07-19 + // - http://www.ngdc.noaa.gov/geomag-web/#declination + *yaw -= 8.5; + *roll *= RAD_TO_DEG; + + ///Serial.printf("%f %f %f \r\n", pitch, roll, yaw); +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; +#pragma GCC diagnostic ignored "-Wstrict-aliasing" + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; +#pragma GCC diagnostic warning "-Wstrict-aliasing" + y = y * (1.5f - (halfx * y * y)); + return y; +} + +//==================================================================================================== +// END OF CODE +//==================================================================================================== diff --git a/src/utility/MahonyAHRS.h b/src/utility/MahonyAHRS.h new file mode 100644 index 0000000..bae082d --- /dev/null +++ b/src/utility/MahonyAHRS.h @@ -0,0 +1,33 @@ +//===================================================================================================== +// MahonyAHRS.h +//===================================================================================================== +// +// Madgwick's implementation of Mayhony's AHRS algorithm. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== +#ifndef MahonyAHRS_h +#define MahonyAHRS_h + +//---------------------------------------------------------------------------------------------------- +// Variable declaration + +extern volatile float twoKp; // 2 * proportional gain (Kp) +extern volatile float twoKi; // 2 * integral gain (Ki) +//volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); +//void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float *pitch,float *roll,float *yaw); +float invSqrt(float x); +#endif +//===================================================================================================== +// End of file +//=====================================================================================================