From e46483716d6e5ad525e3c64ce7a217fc2fec1f4c Mon Sep 17 00:00:00 2001 From: foglar Date: Fri, 15 Mar 2024 15:11:39 +0100 Subject: [PATCH] testing app, management improvements --- README.md | 20 +- monitor/main.go | 4 +- receiver_module/receiver_module.ino | 7 +- sender_module/sender_module.ino | 158 ++---- testing-sender/Waveshare_10Dof-D.cpp | 743 --------------------------- testing-sender/Waveshare_10Dof-D.h | 236 --------- testing-sender/testing-sender.ino | 75 --- testing/gui_app_test/README.md | 3 + testing/gui_app_test/go.mod | 20 + testing/gui_app_test/go.sum | 31 ++ testing/gui_app_test/intuitive.ttf | Bin 0 -> 49552 bytes testing/gui_app_test/main.go | 78 +++ 12 files changed, 192 insertions(+), 1183 deletions(-) delete mode 100644 testing-sender/Waveshare_10Dof-D.cpp delete mode 100644 testing-sender/Waveshare_10Dof-D.h delete mode 100644 testing-sender/testing-sender.ino create mode 100644 testing/gui_app_test/README.md create mode 100644 testing/gui_app_test/go.mod create mode 100644 testing/gui_app_test/go.sum create mode 100644 testing/gui_app_test/intuitive.ttf create mode 100644 testing/gui_app_test/main.go diff --git a/README.md b/README.md index 0b839f8..13c0497 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ go build . Upload sender and reciever code on the 2 arduinos -Required library for antenna: [RF24](https://nrf24.github.io/RF24) +Required library for antenna: [RF24](https://nrf24.github.io/RF24). Installation via Arduino-IDE. ## Overview @@ -71,6 +71,7 @@ Required library for antenna: [RF24](https://nrf24.github.io/RF24) `sender_module/` - folder with code for sender, which transmit data to the reciever and save it on the micro sd card (arduino) `serial_read/` - read serial input and save it `monitor` - folder with code for monitor which will recieve data and print them into the gui application (pc) +`testing/` - other tools and applications ### Schema @@ -93,12 +94,12 @@ Required library for antenna: [RF24](https://nrf24.github.io/RF24) | Identifier | Message Code | Value | Verificator | | ---------- | ------------ | -------------------------------- | ------------| -| $ | **1**; | temperature [degrees of Celsius] | * | -| $ | **2**; | pressure | * | -| $ | **3**; | altitude | * | -| $ | **4**; | roll | * | -| $ | **5**; | pitch | * | -| $ | **6**; | yaw | * | +| $ | **1**; | roll | * | +| $ | **2**; | pitch | * | +| $ | **3**; | yaw | * | +| $ | **4**; | temperature [degrees of Celsius] | * | +| $ | **5**; | pressure | * | +| $ | **6**; | altitude | * | | $ | **7**; | gyroscope x | * | | $ | **8**; | gyroscope y | * | | $ | **9**; | gyroscope z | * | @@ -188,10 +189,12 @@ $GPGAA,HHMMSS.SS,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx - [x] data stops being transmitted from sender after some short period time - [ ] create a version which will send data via **IOT 433MHz LoRa LPWAN SX1278** +- [ ] create a communication in both ways, `start`, `stop`, `system health check` commands +- [ ] detection of apogeum and recovery system launch ### Monitor app issues -- [ ] application crash after some period of time +- [ ] application crash after some period of time, if don't recive any input from serial ports - [ ] gui is not updating until it recieves serial input - [ ] parser should be improved - [ ] sender code should be improved @@ -199,6 +202,7 @@ $GPGAA,HHMMSS.SS,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx - [ ] error messages as windows not terminal - [ ] improve readability of code - [ ] serial monitor setup port and baudrate +- [ ] create a gui way of sending commands ## Sources diff --git a/monitor/main.go b/monitor/main.go index e08ef46..0f2e337 100644 --- a/monitor/main.go +++ b/monitor/main.go @@ -77,14 +77,14 @@ func run() { // Update information if it is in the parsed block if _, ok := info[1]; ok { - temperature_gui = info[1] + temperature_gui = info[4] } win.Clear(colornames.Black) // Print information to text blocks - temperature.WriteString("Temperature: " + temperature_gui) logging_serial.WriteString(data) + temperature.WriteString("Temperature: " + temperature_gui) // Draw information to screen logging_serial.Draw(win, pixel.IM) diff --git a/receiver_module/receiver_module.ino b/receiver_module/receiver_module.ino index 1e25700..bd3de43 100644 --- a/receiver_module/receiver_module.ino +++ b/receiver_module/receiver_module.ino @@ -3,18 +3,23 @@ #include RF24 radio(9, 8); // CE, CSN const byte address[6] = "00001"; //address through which two modules communicate + + void setup() { while (!Serial) ; Serial.begin(9600); - Serial.println("# Reciever Init"); + Serial.println("#200 Reciever Init"); radio.begin(); radio.openReadingPipe(0, address); //set the address radio.startListening(); //Set module as receiver } + + void loop() { //Read the data if available in buffer if (radio.available()) { + Serial.println("#200 Communication is available"); char text[64] = { 0 }; radio.read(&text, sizeof(text)); Serial.println(text); diff --git a/sender_module/sender_module.ino b/sender_module/sender_module.ino index 4f33c47..7a00b77 100644 --- a/sender_module/sender_module.ino +++ b/sender_module/sender_module.ino @@ -2,41 +2,28 @@ #include #include #include "Waveshare_10Dof-D.h" -#include "string.h" - bool gbSenserConnectState = false; -//create an RF24 objetct RF24 radio(9, 8); // CE, CSN -const byte address[6] = "00001"; //address through which two modules communicate +const byte address[6] = "00001"; // address through which two modules communicate void setup() { - // init radio radio.begin(); radio.openWritingPipe(address); - radio.stopListening(); //Set module as transmitter + radio.stopListening(); // Set module as transmitter - // init motion sensor - bool bRet; IMU_EN_SENSOR_TYPE enMotionSensorType, enPressureType; Serial.begin(115200); - imuInit(&enMotionSensorType, &enPressureType); - if(IMU_EN_SENSOR_TYPE_ICM20948 == enMotionSensorType) - { - Serial.println("# Motion sersor is ICM-20948"); + if (IMU_EN_SENSOR_TYPE_ICM20948 == enMotionSensorType) { + Serial.println("# Motion sensor is ICM-20948"); + } else { + Serial.println("# Motion sensor NULL"); } - else - { - Serial.println("# Motion sersor NULL"); - } - if(IMU_EN_SENSOR_TYPE_BMP280 == enPressureType) - { - Serial.println("# Pressure sersor is BMP280"); - } - else - { - Serial.println("# Pressure sersor NULL"); + if (IMU_EN_SENSOR_TYPE_BMP280 == enPressureType) { + Serial.println("# Pressure sensor is BMP280"); + } else { + Serial.println("# Pressure sensor NULL"); } delay(1000); } @@ -48,106 +35,41 @@ void loop() { IMU_ST_SENSOR_DATA stMagnRawData; int32_t s32PressureVal = 0, s32TemperatureVal = 0, s32AltitudeVal = 0; - imuDataGet( &stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData); + imuDataGet(&stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData); pressSensorDataGet(&s32TemperatureVal, &s32PressureVal, &s32AltitudeVal); - char temp_str[8], pressure_str[8], altitude_str[8]; - char roll_str[8], pitch_str[8], yaw_str[8]; - char gyro_x_str[8], gyro_y_str[8], gyro_z_str[8]; - char accel_x_str[8], accel_y_str[8], accel_z_str[8]; - char magn_x_str[8], magn_y_str[8], magn_z_str[8]; + float temperature = s32TemperatureVal / 100.0; + float pressure = s32PressureVal / 100.0; + float altitude = s32AltitudeVal / 100.0; - float temperature = (s32TemperatureVal / 100); - float pressure = (s32PressureVal / 100); - float altitude = (s32AltitudeVal / 100); - - float roll = stAngles.fRoll; - float pitch = stAngles.fPitch; - float yaw = stAngles.fYaw; - - float gyro_x = stGyroRawData.s16X; - float gyro_y = stGyroRawData.s16Y; - float gyro_z = stGyroRawData.s16Z; - - float accel_x = stAccelRawData.s16X; - float accel_y = stAccelRawData.s16Y; - float accel_z = stAccelRawData.s16Z; - - float magn_x = stMagnRawData.s16X; - float magn_y = stMagnRawData.s16Y; - float magn_z = stMagnRawData.s16Z; - - dtostrf(temperature, 6, 2, temp_str); - dtostrf(pressure, 6, 2, pressure_str); - dtostrf(altitude, 6, 2, altitude_str); - - dtostrf(roll, 6, 2, roll_str); - dtostrf(pitch, 6, 2, pitch_str); - dtostrf(yaw, 6, 2, yaw_str); - - dtostrf(gyro_x, 6, 2, gyro_x_str); - dtostrf(gyro_y, 6, 2, gyro_y_str); - dtostrf(gyro_z, 6, 2, gyro_z_str); - - dtostrf(accel_x, 6, 2, accel_x_str); - dtostrf(accel_y, 6, 2, accel_y_str); - dtostrf(accel_z, 6, 2, accel_z_str); - - dtostrf(magn_x, 6, 2, magn_x_str); - dtostrf(magn_y, 6, 2, magn_y_str); - dtostrf(magn_z, 6, 2, magn_z_str); - - //Serial.println(roll, pitch, yaw); - //Serial.println(temperature, pressure, altitude); + float angles[] = {stAngles.fRoll, stAngles.fPitch, stAngles.fYaw}; + float gyro[] = {stGyroRawData.s16X, stGyroRawData.s16Y, stGyroRawData.s16Z}; + float accel[] = {stAccelRawData.s16X, stAccelRawData.s16Y, stAccelRawData.s16Z}; + float magn[] = {stMagnRawData.s16X, stMagnRawData.s16Y, stMagnRawData.s16Z}; char msg[64]; + for (int i = 0; i < 3; i++) { + char float_str[8]; + dtostrf(angles[i], 6, 2, float_str); + String str = String("$") + String(i + 1) + ";" + String(float_str) + "*"; + str.toCharArray(msg, sizeof(msg)); + radio.write(&msg, sizeof(msg)); + } - String ctemp = ("$1;"+String(temp_str)+"*"); - String cpressure = ("$2;"+String(pressure_str)+"*"); - String caltitude = ("$3;"+String(altitude_str)+"*"); - String croll = ("$4;"+String(roll_str)+"*"); - String cpitch = ("$5;"+String(pitch_str)+"*"); - String cyaw = ("$6;"+String(yaw_str) + "*"); - String cgyro_x = ("$7;"+String(gyro_x_str)+"*"); - String cgyro_y = ("$8;"+String(gyro_y_str)+"*"); - String cgyro_z = ("$9;"+String(gyro_z_str)+"*"); - String caccel_x = ("$10;"+String(accel_x_str)+"*"); - String caccel_y = ("$11;"+String(accel_y_str)+"*"); - String caccel_z = ("$12;"+String(accel_z_str)+"*"); - String cmagn_x = ("$13;"+String(magn_x_str)+"*"); - String cmagn_y = ("$14;"+String(magn_y_str)+"*"); - String cmagn_z = ("$15;"+String(magn_z_str)+"*"); + float sensor_data[][3] = { + {temperature, pressure, altitude}, + {gyro[0], gyro[1], gyro[2]}, + {accel[0], accel[1], accel[2]}, + {magn[0], magn[1], magn[2]} + }; - ctemp.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cpressure.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - caltitude.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - croll.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cpitch.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cyaw.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cgyro_x.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cgyro_y.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cgyro_z.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - caccel_x.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - caccel_y.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - caccel_z.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cmagn_x.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cmagn_y.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - cmagn_z.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - - //delay(200); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + char float_str[8]; + dtostrf(sensor_data[i][j], 6, 2, float_str); + String str = String("$") + String(i + 4) + ";" + String(float_str) + "*"; + str.toCharArray(msg, sizeof(msg)); + radio.write(&msg, sizeof(msg)); + } + } } diff --git a/testing-sender/Waveshare_10Dof-D.cpp b/testing-sender/Waveshare_10Dof-D.cpp deleted file mode 100644 index fd05fd2..0000000 --- a/testing-sender/Waveshare_10Dof-D.cpp +++ /dev/null @@ -1,743 +0,0 @@ -/** - ****************************************************************************** - * @file Waveshare_10Dof-D.cpp - * @author Waveshare Team - * @version V1.0 - * @date Dec-2018 - * @brief T - - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, WAVESHARE SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2018 Waveshare

- ****************************************************************************** - */ -#include "Waveshare_10Dof-D.h" -#include - -IMU_ST_SENSOR_DATA gstGyroOffset ={0,0,0}; -#ifdef __cplusplus -extern "C" { -#endif - -void imuAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); -float invSqrt(float x); - -void icm20948init(void); -bool icm20948Check(void); -void icm20948GyroRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z); -void icm20948AccelRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z); -void icm20948MagRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z); -bool icm20948MagCheck(void); -void icm20948CalAvgValue(uint8_t *pIndex, int16_t *pAvgBuffer, int16_t InVal, int32_t *pOutVal); -void icm20948GyroOffset(void); -void icm20948ReadSecondary(uint8_t u8I2CAddr, uint8_t u8RegAddr, uint8_t u8Len, uint8_t *pu8data); -void icm20948WriteSecondary(uint8_t u8I2CAddr, uint8_t u8RegAddr, uint8_t u8data); -bool icm20948Check(void); - -bool bmp280Check(void); -void bmp280Init(void); -/****************************************************************************** - * interface driver * - ******************************************************************************/ -uint8_t I2C_ReadOneByte(uint8_t DevAddr, uint8_t RegAddr) -{ - uint8_t value; - - Wire.beginTransmission(DevAddr); - Wire.write((byte)RegAddr); - Wire.endTransmission(); - - Wire.requestFrom(DevAddr, (byte)1); - value = Wire.read(); - - return value; -} - -void I2C_WriteOneByte(uint8_t DevAddr, uint8_t RegAddr, uint8_t value) -{ - Wire.beginTransmission(DevAddr); - Wire.write(RegAddr); - Wire.write(value); - Wire.endTransmission(); -} -/****************************************************************************** - * IMU module * - ******************************************************************************/ -#define Kp 4.50f // proportional gain governs rate of convergence to accelerometer/magnetometer -#define Ki 1.0f // integral gain governs rate of convergence of gyroscope biases - -float angles[3]; -float q0, q1, q2, q3; - -void imuInit(IMU_EN_SENSOR_TYPE *penMotionSensorType, IMU_EN_SENSOR_TYPE *penPressureType) -{ - bool bRet = false; - Wire.begin(); - bRet = icm20948Check(); - if( true == bRet) - { - *penMotionSensorType = IMU_EN_SENSOR_TYPE_ICM20948; - icm20948init(); - } - else - { - *penMotionSensorType = IMU_EN_SENSOR_TYPE_NULL; - } - - bRet = bmp280Check(); - if( true == bRet) - { - *penPressureType = IMU_EN_SENSOR_TYPE_BMP280; - bmp280Init(); - } - else - { - *penPressureType = IMU_EN_SENSOR_TYPE_NULL; - } - - q0 = 1.0f; - q1 = 0.0f; - q2 = 0.0f; - q3 = 0.0f; - - return; -} - -void imuDataGet(IMU_ST_ANGLES_DATA *pstAngles, - IMU_ST_SENSOR_DATA *pstGyroRawData, - IMU_ST_SENSOR_DATA *pstAccelRawData, - IMU_ST_SENSOR_DATA *pstMagnRawData) -{ - float MotionVal[9]; - int16_t s16Gyro[3], s16Accel[3], s16Magn[3]; - - icm20948AccelRead(&s16Accel[0], &s16Accel[1], &s16Accel[2]); - icm20948GyroRead(&s16Gyro[0], &s16Gyro[1], &s16Gyro[2]); - icm20948MagRead(&s16Magn[0], &s16Magn[1], &s16Magn[2]); - - MotionVal[0]=s16Gyro[0]/32.8; - MotionVal[1]=s16Gyro[1]/32.8; - MotionVal[2]=s16Gyro[2]/32.8; - MotionVal[3]=s16Accel[0]; - MotionVal[4]=s16Accel[1]; - MotionVal[5]=s16Accel[2]; - MotionVal[6]=s16Magn[0]; - MotionVal[7]=s16Magn[1]; - MotionVal[8]=s16Magn[2]; - imuAHRSupdate((float)MotionVal[0] * 0.0175, (float)MotionVal[1] * 0.0175, (float)MotionVal[2] * 0.0175, - (float)MotionVal[3], (float)MotionVal[4], (float)MotionVal[5], - (float)MotionVal[6], (float)MotionVal[7], MotionVal[8]); - - - pstAngles->fPitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch - pstAngles->fRoll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll - pstAngles->fYaw = atan2(-2 * q1 * q2 - 2 * q0 * q3, 2 * q2 * q2 + 2 * q3 * q3 - 1) * 57.3; - - pstGyroRawData->s16X = s16Gyro[0]; - pstGyroRawData->s16Y = s16Gyro[1]; - pstGyroRawData->s16Z = s16Gyro[2]; - - pstAccelRawData->s16X = s16Accel[0]; - pstAccelRawData->s16Y = s16Accel[1]; - pstAccelRawData->s16Z = s16Accel[2]; - - pstMagnRawData->s16X = s16Magn[0]; - pstMagnRawData->s16Y = s16Magn[1]; - pstMagnRawData->s16Z = s16Magn[2]; - - return; -} - -void imuAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) -{ - float norm; - float hx, hy, hz, bx, bz; - float vx, vy, vz, wx, wy, wz; - float exInt = 0.0, eyInt = 0.0, ezInt = 0.0; - float ex, ey, ez, halfT = 0.024f; - - float q0q0 = q0 * q0; - float q0q1 = q0 * q1; - float q0q2 = q0 * q2; - float q0q3 = q0 * q3; - float q1q1 = q1 * q1; - float q1q2 = q1 * q2; - float q1q3 = q1 * q3; - float q2q2 = q2 * q2; - float q2q3 = q2 * q3; - float q3q3 = q3 * q3; - - norm = invSqrt(ax * ax + ay * ay + az * az); - ax = ax * norm; - ay = ay * norm; - az = az * norm; - - norm = invSqrt(mx * mx + my * my + mz * mz); - mx = mx * norm; - my = my * norm; - mz = mz * norm; - - // compute reference direction of flux - hx = 2 * mx * (0.5f - q2q2 - q3q3) + 2 * my * (q1q2 - q0q3) + 2 * mz * (q1q3 + q0q2); - hy = 2 * mx * (q1q2 + q0q3) + 2 * my * (0.5f - q1q1 - q3q3) + 2 * mz * (q2q3 - q0q1); - hz = 2 * mx * (q1q3 - q0q2) + 2 * my * (q2q3 + q0q1) + 2 * mz * (0.5f - q1q1 - q2q2); - bx = sqrt((hx * hx) + (hy * hy)); - bz = hz; - - // estimated direction of gravity and flux (v and w) - vx = 2 * (q1q3 - q0q2); - vy = 2 * (q0q1 + q2q3); - vz = q0q0 - q1q1 - q2q2 + q3q3; - wx = 2 * bx * (0.5 - q2q2 - q3q3) + 2 * bz * (q1q3 - q0q2); - wy = 2 * bx * (q1q2 - q0q3) + 2 * bz * (q0q1 + q2q3); - wz = 2 * bx * (q0q2 + q1q3) + 2 * bz * (0.5 - q1q1 - q2q2); - - // error is sum of cross product between reference direction of fields and direction measured by sensors - ex = (ay * vz - az * vy) + (my * wz - mz * wy); - ey = (az * vx - ax * vz) + (mz * wx - mx * wz); - ez = (ax * vy - ay * vx) + (mx * wy - my * wx); - - if(ex != 0.0f && ey != 0.0f && ez != 0.0f) - { - exInt = exInt + ex * Ki * halfT; - eyInt = eyInt + ey * Ki * halfT; - ezInt = ezInt + ez * Ki * halfT; - - gx = gx + Kp * ex + exInt; - gy = gy + Kp * ey + eyInt; - gz = gz + Kp * ez + ezInt; - } - - q0 = q0 + (-q1 * gx - q2 * gy - q3 * gz) * halfT; - q1 = q1 + (q0 * gx + q2 * gz - q3 * gy) * halfT; - q2 = q2 + (q0 * gy - q1 * gz + q3 * gx) * halfT; - q3 = q3 + (q0 * gz + q1 * gy - q2 * gx) * halfT; - - norm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - q0 = q0 * norm; - q1 = q1 * norm; - q2 = q2 * norm; - q3 = q3 * norm; -} - -float invSqrt(float x) -{ - float halfx = 0.5f * x; - float y = x; - - long i = *(long*)&y; //get bits for floating value - i = 0x5f3759df - (i >> 1); //gives initial guss you - y = *(float*)&i; //convert bits back to float - y = y * (1.5f - (halfx * y * y)); //newtop step, repeating increases accuracy - - return y; -} -/****************************************************************************** - * icm20948 sensor device * - ******************************************************************************/ -void icm20948init(void) -{ - /* user bank 0 register */ - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_REG_BANK_SEL, REG_VAL_REG_BANK_0); - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_PWR_MIGMT_1, REG_VAL_ALL_RGE_RESET); - delay(10); - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_PWR_MIGMT_1, REG_VAL_RUN_MODE); - - /* user bank 2 register */ - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_REG_BANK_SEL, REG_VAL_REG_BANK_2); - I2C_WriteOneByte( I2C_ADD_ICM20948, REG_ADD_GYRO_SMPLRT_DIV, 0x07); - I2C_WriteOneByte( I2C_ADD_ICM20948, REG_ADD_GYRO_CONFIG_1, - REG_VAL_BIT_GYRO_DLPCFG_6 | REG_VAL_BIT_GYRO_FS_1000DPS | REG_VAL_BIT_GYRO_DLPF); - I2C_WriteOneByte( I2C_ADD_ICM20948, REG_ADD_ACCEL_SMPLRT_DIV_2, 0x07); - I2C_WriteOneByte( I2C_ADD_ICM20948, REG_ADD_ACCEL_CONFIG, - REG_VAL_BIT_ACCEL_DLPCFG_6 | REG_VAL_BIT_ACCEL_FS_2g | REG_VAL_BIT_ACCEL_DLPF); - - /* user bank 0 register */ - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_REG_BANK_SEL, REG_VAL_REG_BANK_0); - - delay(100); - /* offset */ - icm20948GyroOffset(); - - icm20948MagCheck(); - - icm20948WriteSecondary( I2C_ADD_ICM20948_AK09916|I2C_ADD_ICM20948_AK09916_WRITE, - REG_ADD_MAG_CNTL2, REG_VAL_MAG_MODE_20HZ); - return; -} - -bool icm20948Check(void) -{ - bool bRet = false; - if(REG_VAL_WIA == I2C_ReadOneByte(I2C_ADD_ICM20948, REG_ADD_WIA)) - { - bRet = true; - } - return bRet; -} -void icm20948GyroRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z) -{ - uint8_t u8Buf[6]; - int16_t s16Buf[3] = {0}; - uint8_t i; - int32_t s32OutBuf[3] = {0}; - static ICM20948_ST_AVG_DATA sstAvgBuf[3]; - static int16_t ss16c = 0; - ss16c++; - - u8Buf[0]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_GYRO_XOUT_L); - u8Buf[1]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_GYRO_XOUT_H); - s16Buf[0]= (u8Buf[1]<<8)|u8Buf[0]; - - u8Buf[0]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_GYRO_YOUT_L); - u8Buf[1]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_GYRO_YOUT_H); - s16Buf[1]= (u8Buf[1]<<8)|u8Buf[0]; - - u8Buf[0]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_GYRO_ZOUT_L); - u8Buf[1]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_GYRO_ZOUT_H); - s16Buf[2]= (u8Buf[1]<<8)|u8Buf[0]; - - for(i = 0; i < 3; i ++) - { - icm20948CalAvgValue(&sstAvgBuf[i].u8Index, sstAvgBuf[i].s16AvgBuffer, s16Buf[i], s32OutBuf + i); - } - *ps16X = s32OutBuf[0] - gstGyroOffset.s16X; - *ps16Y = s32OutBuf[1] - gstGyroOffset.s16Y; - *ps16Z = s32OutBuf[2] - gstGyroOffset.s16Z; - - return; -} -void icm20948AccelRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z) -{ - uint8_t u8Buf[2]; - int16_t s16Buf[3] = {0}; - uint8_t i; - int32_t s32OutBuf[3] = {0}; - static ICM20948_ST_AVG_DATA sstAvgBuf[3]; - - u8Buf[0]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_ACCEL_XOUT_L); - u8Buf[1]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_ACCEL_XOUT_H); - s16Buf[0]= (u8Buf[1]<<8)|u8Buf[0]; - - u8Buf[0]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_ACCEL_YOUT_L); - u8Buf[1]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_ACCEL_YOUT_H); - s16Buf[1]= (u8Buf[1]<<8)|u8Buf[0]; - - u8Buf[0]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_ACCEL_ZOUT_L); - u8Buf[1]=I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_ACCEL_ZOUT_H); - s16Buf[2]= (u8Buf[1]<<8)|u8Buf[0]; - - for(i = 0; i < 3; i ++) - { - icm20948CalAvgValue(&sstAvgBuf[i].u8Index, sstAvgBuf[i].s16AvgBuffer, s16Buf[i], s32OutBuf + i); - } - *ps16X = s32OutBuf[0]; - *ps16Y = s32OutBuf[1]; - *ps16Z = s32OutBuf[2]; - - return; - -} -void icm20948MagRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z) -{ - uint8_t counter = 20; - uint8_t u8Data[MAG_DATA_LEN]; - int16_t s16Buf[3] = {0}; - uint8_t i; - int32_t s32OutBuf[3] = {0}; - static ICM20948_ST_AVG_DATA sstAvgBuf[3]; - while( counter>0 ) - { - delay(10); - icm20948ReadSecondary( I2C_ADD_ICM20948_AK09916|I2C_ADD_ICM20948_AK09916_READ, - REG_ADD_MAG_ST2, 1, u8Data); - - if ((u8Data[0] & 0x01) != 0) - break; - - counter--; - } - - if(counter != 0) - { - icm20948ReadSecondary( I2C_ADD_ICM20948_AK09916|I2C_ADD_ICM20948_AK09916_READ, - REG_ADD_MAG_DATA, - MAG_DATA_LEN, - u8Data); - s16Buf[0] = ((int16_t)u8Data[1]<<8) | u8Data[0]; - s16Buf[1] = ((int16_t)u8Data[3]<<8) | u8Data[2]; - s16Buf[2] = ((int16_t)u8Data[5]<<8) | u8Data[4]; - } - - for(i = 0; i < 3; i ++) - { - icm20948CalAvgValue(&sstAvgBuf[i].u8Index, sstAvgBuf[i].s16AvgBuffer, s16Buf[i], s32OutBuf + i); - } - - *ps16X = s32OutBuf[0]; - *ps16Y = -s32OutBuf[1]; - *ps16Z = -s32OutBuf[2]; - return; -} - -void icm20948ReadSecondary(uint8_t u8I2CAddr, uint8_t u8RegAddr, uint8_t u8Len, uint8_t *pu8data) -{ - uint8_t i; - uint8_t u8Temp; - - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_REG_BANK_SEL, REG_VAL_REG_BANK_3); //swtich bank3 - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_I2C_SLV0_ADDR, u8I2CAddr); - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_I2C_SLV0_REG, u8RegAddr); - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_I2C_SLV0_CTRL, REG_VAL_BIT_SLV0_EN|u8Len); - - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_REG_BANK_SEL, REG_VAL_REG_BANK_0); //swtich bank0 - - u8Temp = I2C_ReadOneByte(I2C_ADD_ICM20948,REG_ADD_USER_CTRL); - u8Temp |= REG_VAL_BIT_I2C_MST_EN; - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_USER_CTRL, u8Temp); - delay(5); - u8Temp &= ~REG_VAL_BIT_I2C_MST_EN; - I2C_WriteOneByte(I2C_ADD_ICM20948, REG_ADD_USER_CTRL, u8Temp); - - for(i=0; i>= 3; -} - -void icm20948GyroOffset(void) -{ - uint8_t i; - int16_t s16Gx = 0, s16Gy = 0, s16Gz = 0; - int32_t s32TempGx = 0, s32TempGy = 0, s32TempGz = 0; - for(i = 0; i < 32; i ++) - { - icm20948GyroRead(&s16Gx, &s16Gy, &s16Gz); - s32TempGx += s16Gx; - s32TempGy += s16Gy; - s32TempGz += s16Gz; - delay(10); - } - gstGyroOffset.s16X = s32TempGx >> 5; - gstGyroOffset.s16Y = s32TempGy >> 5; - gstGyroOffset.s16Z = s32TempGz >> 5; - return; -} - -bool icm20948MagCheck(void) -{ - bool bRet = false; - uint8_t u8Ret[2]; - - icm20948ReadSecondary( I2C_ADD_ICM20948_AK09916|I2C_ADD_ICM20948_AK09916_READ, - REG_ADD_MAG_WIA1, 2,u8Ret); - if( (u8Ret[0] == REG_VAL_MAG_WIA1) && ( u8Ret[1] == REG_VAL_MAG_WIA2) ) - { - bRet = true; - } - - return bRet; -} - -/****************************************************************************** - * BMP280 sensor device * - ******************************************************************************/ - typedef struct { - uint16_t T1; /*>3) - ((int64_t)dig_T1 <<1))) *((int64_t)dig_T2)) >> 11; - var2 = (((((adc_T>>4) - ((int64_t)dig_T1)) *((adc_T>>4) - ((int64_t)dig_T1))) >> 12) * - ((int64_t)dig_T3)) >> 14; - t_fine = var1 + var2; - - temperature = (t_fine * 5 + 128) >> 8; - - return (float)temperature; -} - -float bmp280CompensatePressure(int32_t adc_P) -{ - int64_t var1, var2; - uint64_t pressure; -#if 1 - var1 = ((int64_t)t_fine) - 128000; - var2 = var1 * var1 * (int64_t)dig_P6; - var2 = var2 + ((var1*(int64_t)dig_P5)<<17); - var2 = var2 + (((int64_t)dig_P4)<<35); - var1 = ((var1 * var1 * (int64_t)dig_P3)>>8) + ((var1 * (int64_t)dig_P2)<<12); - var1 = (((((int64_t)1)<<47)+var1))*((int64_t)dig_P1)>>33; - - if (var1 == 0) { - return 0; // avoid exception caused by division by zero - } - - pressure = 1048576.0 - adc_P; - pressure = (((pressure<<31) - var2)*3125) / var1; - var1 = (((int64_t)dig_P9) * (pressure>>13) * (pressure>>13)) >> 25; - var2 = (((int64_t)dig_P8) * pressure) >> 19; - pressure = ((pressure + var1 + var2) >> 8) + (((int64_t)dig_P7)<<4); - return (float)pressure/256; -#else - var1 = (((int64_t)t_fine)>>1) - (int64_t)64000; - var2 = (((var1>>2) * (var1>>2)) >> 11 ) *((int64_t)dig_P6); - var2 = var2 + ((var1 *((int64_t)dig_P5))<<1); - var2 = (var2>>2) + (((int64_t)dig_P4)<<16); - var1 = (((dig_P3 * (((var1>>2) * (var1>>2))>>13))>>3) + ((((int64_t)dig_P2) * var1)>>1))>>18; - var1 = ((((32768+var1))*((int64_t)dig_P1))>>15); - if(var1 ==0) - { - return 0; - } - pressure = (1048576.0 - adc_P) - (var2>>12)*3125; - if(pressure < 0x80000000) - { - pressure = (pressure<<1)/((uint64_t)var1); - } - else - { - pressure = (pressure/(uint64_t)var1)*2; - } - var1 = (((int64_t)dig_P9) *((int64_t)(((pressure>>3)*(pressure>>3))>>13)))>>12; - var2 = (((int64_t)(pressure>>2))*((int64_t)dig_P8))>>13; - pressure = (uint64_t)((int64_t)pressure) +((var1 + var2 + dig_P7)>>4); - return (float)pressure; -#endif - -} - -void bmp280TandPGet(float *temperature, float *pressure) -{ - uint8_t lsb, msb, xlsb; - int32_t adc_P,adc_T; - - xlsb = I2C_ReadOneByte(BMP280_ADDR, BMP280_TEMP_XLSB_REG); - lsb = I2C_ReadOneByte(BMP280_ADDR, BMP280_TEMP_LSB_REG); - msb = I2C_ReadOneByte(BMP280_ADDR, BMP280_TEMP_MSB_REG); - //adc_T = (msb << 12) | (lsb << 4) | (xlsb >> 4); - adc_T = msb; - adc_T <<= 8; - adc_T |= lsb; - adc_T <<= 8; - adc_T |= xlsb; - adc_T >>= 4; - //adc_T = 415148; - *temperature = bmp280CompensateTemperature(adc_T); - - xlsb = I2C_ReadOneByte(BMP280_ADDR, BMP280_PRESS_XLSB_REG); - lsb = I2C_ReadOneByte(BMP280_ADDR, BMP280_PRESS_LSB_REG); - msb = I2C_ReadOneByte(BMP280_ADDR, BMP280_PRESS_MSB_REG); - //adc_P = (msb << 12) | (lsb << 4) | (xlsb >> 4); - adc_P = msb; - adc_P <<= 8; - adc_P |= lsb; - adc_P <<= 8; - adc_P |= xlsb; - adc_P >>= 4; - //adc_P = 51988; - *pressure = bmp280CompensatePressure(adc_P); -} - -void bmp280CalAvgValue(uint8_t *pIndex, int32_t *pAvgBuffer, int32_t InVal, int32_t *pOutVal) -{ - uint8_t i; - - *(pAvgBuffer + ((*pIndex) ++)) = InVal; - *pIndex &= 0x07; - - *pOutVal = 0; - for(i = 0; i < 8; i ++) - { - *pOutVal += *(pAvgBuffer + i); - } - *pOutVal >>= 3; -} - -void bmp280CalculateAbsoluteAltitude(int32_t *pAltitude, int32_t PressureVal) -{ - *pAltitude = 4433000 * (1 - pow((PressureVal / (float)gs32Pressure0), 0.1903)); -} - -void pressSensorDataGet(int32_t *ps32Temperature, int32_t *ps32Pressure, int32_t *ps32Altitude) -{ - float CurPressure, CurTemperature; - int32_t CurAltitude; - static BMP280_AvgTypeDef BMP280_Filter[3]; - - bmp280TandPGet(&CurTemperature, &CurPressure); - bmp280CalAvgValue(&BMP280_Filter[0].Index, BMP280_Filter[0].AvgBuffer, (int32_t)(CurPressure), ps32Pressure); - - bmp280CalculateAbsoluteAltitude(&CurAltitude, (*ps32Pressure)); - bmp280CalAvgValue(&BMP280_Filter[1].Index, BMP280_Filter[1].AvgBuffer, CurAltitude, ps32Altitude); - bmp280CalAvgValue(&BMP280_Filter[2].Index, BMP280_Filter[2].AvgBuffer, (int32_t)CurTemperature, ps32Temperature); - return; -} - -#ifdef __cplusplus -} -#endif diff --git a/testing-sender/Waveshare_10Dof-D.h b/testing-sender/Waveshare_10Dof-D.h deleted file mode 100644 index dfbd978..0000000 --- a/testing-sender/Waveshare_10Dof-D.h +++ /dev/null @@ -1,236 +0,0 @@ -/** - ****************************************************************************** - * @file Waveshare_10Dof-D.h - * @author Waveshare Team - * @version V1.0 - * @date Dec-2018 - * @brief - - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, WAVESHARE SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2018 Waveshare

- ****************************************************************************** - */ -#ifndef __Waveshare_10DOF_D_H__ -#define __Waveshare_10DOF_D_H__ -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -/* define ICM-20948 Device I2C address*/ -#define I2C_ADD_ICM20948 0x68 -#define I2C_ADD_ICM20948_AK09916 0x0C -#define I2C_ADD_ICM20948_AK09916_READ 0x80 -#define I2C_ADD_ICM20948_AK09916_WRITE 0x00 -/* define ICM-20948 Register */ -/* user bank 0 register */ -#define REG_ADD_WIA 0x00 - #define REG_VAL_WIA 0xEA -#define REG_ADD_USER_CTRL 0x03 - #define REG_VAL_BIT_DMP_EN 0x80 - #define REG_VAL_BIT_FIFO_EN 0x40 - #define REG_VAL_BIT_I2C_MST_EN 0x20 - #define REG_VAL_BIT_I2C_IF_DIS 0x10 - #define REG_VAL_BIT_DMP_RST 0x08 - #define REG_VAL_BIT_DIAMOND_DMP_RST 0x04 -#define REG_ADD_PWR_MIGMT_1 0x06 - #define REG_VAL_ALL_RGE_RESET 0x80 - #define REG_VAL_RUN_MODE 0x01 //Non low-power mode -#define REG_ADD_LP_CONFIG 0x05 -#define REG_ADD_PWR_MGMT_1 0x06 -#define REG_ADD_PWR_MGMT_2 0x07 -#define REG_ADD_ACCEL_XOUT_H 0x2D -#define REG_ADD_ACCEL_XOUT_L 0x2E -#define REG_ADD_ACCEL_YOUT_H 0x2F -#define REG_ADD_ACCEL_YOUT_L 0x30 -#define REG_ADD_ACCEL_ZOUT_H 0x31 -#define REG_ADD_ACCEL_ZOUT_L 0x32 -#define REG_ADD_GYRO_XOUT_H 0x33 -#define REG_ADD_GYRO_XOUT_L 0x34 -#define REG_ADD_GYRO_YOUT_H 0x35 -#define REG_ADD_GYRO_YOUT_L 0x36 -#define REG_ADD_GYRO_ZOUT_H 0x37 -#define REG_ADD_GYRO_ZOUT_L 0x38 -#define REG_ADD_EXT_SENS_DATA_00 0x3B -#define REG_ADD_REG_BANK_SEL 0x7F - #define REG_VAL_REG_BANK_0 0x00 - #define REG_VAL_REG_BANK_1 0x10 - #define REG_VAL_REG_BANK_2 0x20 - #define REG_VAL_REG_BANK_3 0x30 - -/* user bank 1 register */ -/* user bank 2 register */ -#define REG_ADD_GYRO_SMPLRT_DIV 0x00 -#define REG_ADD_GYRO_CONFIG_1 0x01 - #define REG_VAL_BIT_GYRO_DLPCFG_2 0x10 /* bit[5:3] */ - #define REG_VAL_BIT_GYRO_DLPCFG_4 0x20 /* bit[5:3] */ - #define REG_VAL_BIT_GYRO_DLPCFG_6 0x30 /* bit[5:3] */ - #define REG_VAL_BIT_GYRO_FS_250DPS 0x00 /* bit[2:1] */ - #define REG_VAL_BIT_GYRO_FS_500DPS 0x02 /* bit[2:1] */ - #define REG_VAL_BIT_GYRO_FS_1000DPS 0x04 /* bit[2:1] */ - #define REG_VAL_BIT_GYRO_FS_2000DPS 0x06 /* bit[2:1] */ - #define REG_VAL_BIT_GYRO_DLPF 0x01 /* bit[0] */ -#define REG_ADD_ACCEL_SMPLRT_DIV_2 0x11 -#define REG_ADD_ACCEL_CONFIG 0x14 - #define REG_VAL_BIT_ACCEL_DLPCFG_2 0x10 /* bit[5:3] */ - #define REG_VAL_BIT_ACCEL_DLPCFG_4 0x20 /* bit[5:3] */ - #define REG_VAL_BIT_ACCEL_DLPCFG_6 0x30 /* bit[5:3] */ - #define REG_VAL_BIT_ACCEL_FS_2g 0x00 /* bit[2:1] */ - #define REG_VAL_BIT_ACCEL_FS_4g 0x02 /* bit[2:1] */ - #define REG_VAL_BIT_ACCEL_FS_8g 0x04 /* bit[2:1] */ - #define REG_VAL_BIT_ACCEL_FS_16g 0x06 /* bit[2:1] */ - #define REG_VAL_BIT_ACCEL_DLPF 0x01 /* bit[0] */ - -/* user bank 3 register */ -#define REG_ADD_I2C_SLV0_ADDR 0x03 -#define REG_ADD_I2C_SLV0_REG 0x04 -#define REG_ADD_I2C_SLV0_CTRL 0x05 - #define REG_VAL_BIT_SLV0_EN 0x80 - #define REG_VAL_BIT_MASK_LEN 0x07 -#define REG_ADD_I2C_SLV0_DO 0x06 -#define REG_ADD_I2C_SLV1_ADDR 0x07 -#define REG_ADD_I2C_SLV1_REG 0x08 -#define REG_ADD_I2C_SLV1_CTRL 0x09 -#define REG_ADD_I2C_SLV1_DO 0x0A - -/* define ICM-20948 Register end */ - -/* define ICM-20948 MAG Register */ -#define REG_ADD_MAG_WIA1 0x00 - #define REG_VAL_MAG_WIA1 0x48 -#define REG_ADD_MAG_WIA2 0x01 - #define REG_VAL_MAG_WIA2 0x09 -#define REG_ADD_MAG_ST2 0x10 -#define REG_ADD_MAG_DATA 0x11 -#define REG_ADD_MAG_CNTL2 0x31 - #define REG_VAL_MAG_MODE_PD 0x00 - #define REG_VAL_MAG_MODE_SM 0x01 - #define REG_VAL_MAG_MODE_10HZ 0x02 - #define REG_VAL_MAG_MODE_20HZ 0x04 - #define REG_VAL_MAG_MODE_50HZ 0x05 - #define REG_VAL_MAG_MODE_100HZ 0x08 - #define REG_VAL_MAG_MODE_ST 0x10 -/* define ICM-20948 MAG Register end */ - -#define MAG_DATA_LEN 6 - -/* - * BMP280 I2c address - */ -#define BMP280_AD0_LOW 0x76 //address pin low (GND) -#define BMP280_AD0_HIGH 0x77 //address pin high (VCC) -#define BMP280_ADDR BMP280_AD0_HIGH // default I2C address -/* - * BMP280 register address - */ -#define BMP280_REGISTER_DIG_T1 0x88 -#define BMP280_REGISTER_DIG_T2 0x8A -#define BMP280_REGISTER_DIG_T3 0x8C - -#define BMP280_REGISTER_DIG_P1 0x8E -#define BMP280_REGISTER_DIG_P2 0x90 -#define BMP280_REGISTER_DIG_P3 0x92 -#define BMP280_REGISTER_DIG_P4 0x94 -#define BMP280_REGISTER_DIG_P5 0x96 -#define BMP280_REGISTER_DIG_P6 0x98 -#define BMP280_REGISTER_DIG_P7 0x9A -#define BMP280_REGISTER_DIG_P8 0x9C -#define BMP280_REGISTER_DIG_P9 0x9E - -#define BMP280_REGISTER_CHIPID 0xD0 -#define BMP280_REGISTER_VERSION 0xD1 -#define BMP280_REGISTER_SOFTRESET 0xE0 -#define BMP280_REGISTER_STATUS 0xF3 -#define BMP280_REGISTER_CONTROL 0xF4 -#define BMP280_REGISTER_CONFIG 0xF5 - -#define BMP280_TEMP_XLSB_REG 0xFC /*Temperature XLSB Register */ -#define BMP280_TEMP_LSB_REG 0xFB /*Temperature LSB Register */ -#define BMP280_TEMP_MSB_REG 0xFA /*Temperature LSB Register */ -#define BMP280_PRESS_XLSB_REG 0xF9 /*Pressure XLSB Register */ -#define BMP280_PRESS_LSB_REG 0xF8 /*Pressure LSB Register */ -#define BMP280_PRESS_MSB_REG 0xF7 /*Pressure MSB Register */ - -/*calibration parameters */ -#define BMP280_DIG_T1_LSB_REG 0x88 -#define BMP280_DIG_T1_MSB_REG 0x89 -#define BMP280_DIG_T2_LSB_REG 0x8A -#define BMP280_DIG_T2_MSB_REG 0x8B -#define BMP280_DIG_T3_LSB_REG 0x8C -#define BMP280_DIG_T3_MSB_REG 0x8D -#define BMP280_DIG_P1_LSB_REG 0x8E -#define BMP280_DIG_P1_MSB_REG 0x8F -#define BMP280_DIG_P2_LSB_REG 0x90 -#define BMP280_DIG_P2_MSB_REG 0x91 -#define BMP280_DIG_P3_LSB_REG 0x92 -#define BMP280_DIG_P3_MSB_REG 0x93 -#define BMP280_DIG_P4_LSB_REG 0x94 -#define BMP280_DIG_P4_MSB_REG 0x95 -#define BMP280_DIG_P5_LSB_REG 0x96 -#define BMP280_DIG_P5_MSB_REG 0x97 -#define BMP280_DIG_P6_LSB_REG 0x98 -#define BMP280_DIG_P6_MSB_REG 0x99 -#define BMP280_DIG_P7_LSB_REG 0x9A -#define BMP280_DIG_P7_MSB_REG 0x9B -#define BMP280_DIG_P8_LSB_REG 0x9C -#define BMP280_DIG_P8_MSB_REG 0x9D -#define BMP280_DIG_P9_LSB_REG 0x9E -#define BMP280_DIG_P9_MSB_REG 0x9F - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum -{ - IMU_EN_SENSOR_TYPE_NULL = 0, - IMU_EN_SENSOR_TYPE_ICM20948, - IMU_EN_SENSOR_TYPE_BMP280, - IMU_EN_SENSOR_TYPE_MAX -}IMU_EN_SENSOR_TYPE; - -typedef struct imu_st_angles_data_tag -{ - float fYaw; - float fPitch; - float fRoll; -}IMU_ST_ANGLES_DATA; - -typedef struct imu_st_sensor_data_tag -{ - int16_t s16X; - int16_t s16Y; - int16_t s16Z; -}IMU_ST_SENSOR_DATA; - -typedef struct icm20948_st_avg_data_tag -{ - uint8_t u8Index; - int16_t s16AvgBuffer[8]; -}ICM20948_ST_AVG_DATA; - -void imuInit(IMU_EN_SENSOR_TYPE *penMotionSensorType, IMU_EN_SENSOR_TYPE *penPressureType); -void imuDataGet(IMU_ST_ANGLES_DATA *pstAngles, - IMU_ST_SENSOR_DATA *pstGyroRawData, - IMU_ST_SENSOR_DATA *pstAccelRawData, - IMU_ST_SENSOR_DATA *pstMagnRawData); -void pressSensorDataGet(int32_t *ps32Temperature, int32_t *ps32Pressure, int32_t *ps32Altitude); - -uint8_t I2C_ReadOneByte(uint8_t DevAddr, uint8_t RegAddr); -void I2C_WriteOneByte(uint8_t DevAddr, uint8_t RegAddr, uint8_t value); - -#ifdef __cplusplus -} -#endif - -#endif //__Waveshare_10DOF_D_H__ diff --git a/testing-sender/testing-sender.ino b/testing-sender/testing-sender.ino deleted file mode 100644 index 7a00b77..0000000 --- a/testing-sender/testing-sender.ino +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include "Waveshare_10Dof-D.h" - -bool gbSenserConnectState = false; -RF24 radio(9, 8); // CE, CSN -const byte address[6] = "00001"; // address through which two modules communicate - -void setup() { - radio.begin(); - radio.openWritingPipe(address); - radio.stopListening(); // Set module as transmitter - - IMU_EN_SENSOR_TYPE enMotionSensorType, enPressureType; - Serial.begin(115200); - imuInit(&enMotionSensorType, &enPressureType); - if (IMU_EN_SENSOR_TYPE_ICM20948 == enMotionSensorType) { - Serial.println("# Motion sensor is ICM-20948"); - } else { - Serial.println("# Motion sensor NULL"); - } - if (IMU_EN_SENSOR_TYPE_BMP280 == enPressureType) { - Serial.println("# Pressure sensor is BMP280"); - } else { - Serial.println("# Pressure sensor NULL"); - } - delay(1000); -} - -void loop() { - IMU_ST_ANGLES_DATA stAngles; - IMU_ST_SENSOR_DATA stGyroRawData; - IMU_ST_SENSOR_DATA stAccelRawData; - IMU_ST_SENSOR_DATA stMagnRawData; - int32_t s32PressureVal = 0, s32TemperatureVal = 0, s32AltitudeVal = 0; - - imuDataGet(&stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData); - pressSensorDataGet(&s32TemperatureVal, &s32PressureVal, &s32AltitudeVal); - - float temperature = s32TemperatureVal / 100.0; - float pressure = s32PressureVal / 100.0; - float altitude = s32AltitudeVal / 100.0; - - float angles[] = {stAngles.fRoll, stAngles.fPitch, stAngles.fYaw}; - float gyro[] = {stGyroRawData.s16X, stGyroRawData.s16Y, stGyroRawData.s16Z}; - float accel[] = {stAccelRawData.s16X, stAccelRawData.s16Y, stAccelRawData.s16Z}; - float magn[] = {stMagnRawData.s16X, stMagnRawData.s16Y, stMagnRawData.s16Z}; - - char msg[64]; - for (int i = 0; i < 3; i++) { - char float_str[8]; - dtostrf(angles[i], 6, 2, float_str); - String str = String("$") + String(i + 1) + ";" + String(float_str) + "*"; - str.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - } - - float sensor_data[][3] = { - {temperature, pressure, altitude}, - {gyro[0], gyro[1], gyro[2]}, - {accel[0], accel[1], accel[2]}, - {magn[0], magn[1], magn[2]} - }; - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 3; j++) { - char float_str[8]; - dtostrf(sensor_data[i][j], 6, 2, float_str); - String str = String("$") + String(i + 4) + ";" + String(float_str) + "*"; - str.toCharArray(msg, sizeof(msg)); - radio.write(&msg, sizeof(msg)); - } - } -} diff --git a/testing/gui_app_test/README.md b/testing/gui_app_test/README.md new file mode 100644 index 0000000..e4e7a71 --- /dev/null +++ b/testing/gui_app_test/README.md @@ -0,0 +1,3 @@ +# gui_app_test + +- preview of the gui app without connection to arduino diff --git a/testing/gui_app_test/go.mod b/testing/gui_app_test/go.mod new file mode 100644 index 0000000..6479ec7 --- /dev/null +++ b/testing/gui_app_test/go.mod @@ -0,0 +1,20 @@ +module test/test + +go 1.21 + +toolchain go1.22.1 + +require ( + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/gopxl/pixel v1.0.0 + golang.org/x/image v0.15.0 +) + +require ( + github.com/faiface/glhf v0.0.0-20211013000516-57b20770c369 // indirect + github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // indirect + github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect + github.com/go-gl/mathgl v1.1.0 // indirect + github.com/pkg/errors v0.9.1 // indirect +) diff --git a/testing/gui_app_test/go.sum b/testing/gui_app_test/go.sum new file mode 100644 index 0000000..432a725 --- /dev/null +++ b/testing/gui_app_test/go.sum @@ -0,0 +1,31 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/faiface/glhf v0.0.0-20211013000516-57b20770c369 h1:gv4BgP50atccdK/1tZHDyP6rMwiiutR2HPreR/OyLzI= +github.com/faiface/glhf v0.0.0-20211013000516-57b20770c369/go.mod h1:dDdUO+G9ZnJ9sc8nIUvhLkE45k8PEKW6+A3TdWsfpV0= +github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q= +github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M= +github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= +github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= +github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= +github.com/go-gl/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/mathgl v1.0.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/go-gl/mathgl v1.1.0 h1:0lzZ+rntPX3/oGrDzYGdowSLC2ky8Osirvf5uAwfIEA= +github.com/go-gl/mathgl v1.1.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/gopxl/pixel v1.0.0 h1:ZON6ll6/tI6sO8fwrlj93GVUcXReTST5//iKv6lcd8g= +github.com/gopxl/pixel v1.0.0/go.mod h1:kPUBG2He7/+alwmi5z0IwnpAc6pw2N7eA08cdBfoE/Q= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= +golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/testing/gui_app_test/intuitive.ttf b/testing/gui_app_test/intuitive.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9039d7b3e5e9bd7830b19102b1ba5d1c37599d0b GIT binary patch literal 49552 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 literal 0 HcmV?d00001 diff --git a/testing/gui_app_test/main.go b/testing/gui_app_test/main.go new file mode 100644 index 0000000..029a2e2 --- /dev/null +++ b/testing/gui_app_test/main.go @@ -0,0 +1,78 @@ +package main + +import ( + "io" + "os" + "time" + + "github.com/golang/freetype/truetype" + "github.com/gopxl/pixel" + "github.com/gopxl/pixel/pixelgl" + "github.com/gopxl/pixel/text" + "golang.org/x/image/colornames" + "golang.org/x/image/font" +) + +func loadTTF(path string, size float64) (font.Face, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + bytes, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + font, err := truetype.Parse(bytes) + if err != nil { + return nil, err + } + + return truetype.NewFace(font, &truetype.Options{ + Size: size, + GlyphCacheEntries: 1, + }), nil +} + +func run() { + cfg := pixelgl.WindowConfig{ + Title: "Cobra Monitor", + Bounds: pixel.R(0, 0, 1024, 768), + } + win, err := pixelgl.NewWindow(cfg) + if err != nil { + panic(err) + } + win.SetSmooth(true) + + face, err := loadTTF("intuitive.ttf", 80) + if err != nil { + panic(err) + } + + atlas := text.NewAtlas(face, text.ASCII) + txt := text.New(pixel.V(50, 500), atlas) + + txt.Color = colornames.Lightgrey + + fps := time.Tick(time.Second / 120) + + for !win.Closed() { + txt.WriteString(win.Typed()) + if win.JustPressed(pixelgl.KeyEnter) || win.Repeated(pixelgl.KeyEnter) { + txt.WriteRune('\n') + } + + win.Clear(colornames.Darkcyan) + txt.Draw(win, pixel.IM.Moved(win.Bounds().Center().Sub(txt.Bounds().Center()))) + win.Update() + + <-fps + } +} + +func main() { + pixelgl.Run(run) +}