├── .gitmodules ├── LICENSE ├── README.md ├── firmware-arduino └── esp_10dof │ ├── bmp280.cpp │ ├── bmp280.h │ ├── esp_10dof.ino │ ├── imu.h │ ├── imu.ino │ ├── lsm9ds1.h │ ├── lsm9ds1.ino │ ├── lsm9ds1_reg.h │ └── lsm9ds1_types.h ├── firmware-c ├── Makefile ├── user.cfg ├── user │ ├── bmp280.h │ ├── custom_commands.c │ ├── i2c.c │ ├── i2c.h │ ├── lsm9ds1.h │ ├── promiscuous.c │ ├── promiscuous.h │ ├── static_i2c.h │ └── user_main.c └── web │ ├── Makefile │ ├── execute_reflash.c │ ├── md5.c │ ├── md5.h │ ├── mfsmaker.c │ ├── page │ ├── index.html │ ├── jquery-2.1.4.min.js.gz │ └── menuinterface.js │ └── pushtodev.c └── hardware ├── ESPLocalizerDec2016.JPG ├── PinoutDesign.png ├── esplocalizer-tight-F-cache.lib ├── esplocalizer-tight-F.pdf └── esplocalizer-tight-F.sch /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "firmware-c/esp82xx"] 2 | path = firmware-c/esp82xx 3 | url = https://github.com/cnlohr/esp82xx.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jorge Marin, Charles Lohr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esplocalizer 2 | 3 | ##ESP Localizer Board 4 | The esplocalizer-tight design is open source and leverages the versatility of the ESP8266 microprocessor and WiFi along with the 10 degrees of freedom sensors and integrated battery controller. The unit comes with basic sensor code loaded and tested on the board. 5 | 6 | ![Physical ESPLocalizer](https://github.com/cnlohr/esplocalizer/raw/master/hardware/ESPLocalizerDec2016.JPG) 7 | 8 | Ebay link: http://www.ebay.com/itm/112253286481 9 | 10 | ##ESP-12-F: 11 | ESP-12F WiFi module is developed by Ai-thinker Team. core processor ESP8266 in smaller sizes of the module encapsulates Tensilica L106 integrates industry-leading ultra low power 32-bit MCU micro, with the 16-bit short mode, Clock speed support 80 MHz, 160 MHz, supports the RTOS, integrated Wi-Fi MAC/BB/RF/PA/LNA, on-board antenna. The module supports standard IEEE802.11 b/g/n agreement, complete TCP/IP protocol stack. Users can use the add modules to an existing device networking, or building a separate network controller. ESP8266 is high integration wireless SOCs, designed for space and power constrained mobile platform designers. It provides unsurpassed ability to embed Wi-Fi capabilities within other systems, or to function as a standalone application, with the lowest cost, and minimal space requirement. 12 | 13 | ##BMP280: 14 | BMP280 is an absolute barometric pressure sensor especially designed for mobile applications. The sensor module is housed in an extremely compact package. Its small dimensions and its low power consumption allow for the implementation in battery driven devices such as mobile phones, GPS modules or watches 15 | 16 | ##LSM9DS1: 17 | The LSM9DS1 is a versatile, motion-sensing system-in-a-chip. It houses a 3-axis accelerometer, 3-axis gyroscope, and 3-axis magnetometer – nine degrees of freedom (9DOF) in a single IC. 18 | 19 | ##STBC08: 20 | The STBC08 is a constant current/constant voltage charger for single-cell Li-Ion batteries. The ESPLocalizer comes with a connector for hubsan-type lithium ion batteries. 21 | 22 | # Resources 23 | 24 | ESP8266.com forums: http://www.esp8266.com/viewforum.php?f=161 25 | 26 | Pinout: 27 | ![ESPLocalizer pinout](https://github.com/cnlohr/esplocalizer/raw/master/hardware/PinoutDesign.png) 28 | -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/bmp280.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bmp280.ino 3 | * @brief Library for interfacing with the BMP280 4 | * (Based on library written by Adafruit) 5 | */ 6 | 7 | 8 | #include "bmp280.h" 9 | #include 10 | #include 11 | 12 | /*************************************************************************** 13 | PRIVATE FUNCTIONS 14 | ***************************************************************************/ 15 | 16 | 17 | BMP280::BMP280() {} 18 | 19 | bool BMP280::begin(uint8_t a, uint8_t chipid) { 20 | _i2caddr = a; 21 | 22 | Wire.begin(5, 4); // Initialize I2C library 23 | 24 | Serial.println (read8(BMP280_REGISTER_CHIPID)); 25 | 26 | if (read8(BMP280_REGISTER_CHIPID) != chipid) 27 | return false; 28 | 29 | readCoefficients(); 30 | write8(BMP280_REGISTER_CONTROL, 0x3F); 31 | return true; 32 | } 33 | 34 | /**************************************************************************/ 35 | /*! 36 | @brief Writes an 8 bit value over I2C/SPI 37 | */ 38 | /**************************************************************************/ 39 | void BMP280::write8(byte reg, byte value) 40 | { 41 | Wire.beginTransmission((uint8_t)_i2caddr); 42 | Wire.write((uint8_t)reg); 43 | Wire.write((uint8_t)value); 44 | Wire.endTransmission(); 45 | } 46 | 47 | /**************************************************************************/ 48 | /*! 49 | @brief Reads an 8 bit value over I2C 50 | */ 51 | /**************************************************************************/ 52 | uint8_t BMP280::read8(byte reg) 53 | { 54 | uint8_t value; 55 | 56 | Wire.beginTransmission((uint8_t)_i2caddr); 57 | Wire.write((uint8_t)reg); 58 | Wire.endTransmission(); 59 | Wire.requestFrom((uint8_t)_i2caddr, (byte)1); 60 | value = Wire.read(); 61 | return value; 62 | } 63 | 64 | /**************************************************************************/ 65 | /*! 66 | @brief Reads a 16 bit value over I2C 67 | */ 68 | /**************************************************************************/ 69 | uint16_t BMP280::read16(byte reg) 70 | { 71 | uint16_t value; 72 | 73 | Wire.beginTransmission((uint8_t)_i2caddr); 74 | Wire.write((uint8_t)reg); 75 | Wire.endTransmission(); 76 | Wire.requestFrom((uint8_t)_i2caddr, (byte)2); 77 | value = (Wire.read() << 8) | Wire.read(); 78 | 79 | return value; 80 | } 81 | 82 | uint16_t BMP280::read16_LE(byte reg) { 83 | uint16_t temp = read16(reg); 84 | return (temp >> 8) | (temp << 8); 85 | } 86 | 87 | /**************************************************************************/ 88 | /*! 89 | @brief Reads a signed 16 bit value over I2C 90 | */ 91 | /**************************************************************************/ 92 | int16_t BMP280::readS16(byte reg) 93 | { 94 | return (int16_t)read16(reg); 95 | 96 | } 97 | 98 | int16_t BMP280::readS16_LE(byte reg) 99 | { 100 | return (int16_t)read16_LE(reg); 101 | 102 | } 103 | 104 | 105 | /**************************************************************************/ 106 | /*! 107 | @brief Reads a signed 16 bit value over I2C 108 | */ 109 | /**************************************************************************/ 110 | 111 | uint32_t BMP280::read24(byte reg) 112 | { 113 | uint32_t value; 114 | 115 | Wire.beginTransmission((uint8_t)_i2caddr); 116 | Wire.write((uint8_t)reg); 117 | Wire.endTransmission(); 118 | Wire.requestFrom((uint8_t)_i2caddr, (byte)3); 119 | 120 | value = Wire.read(); 121 | value <<= 8; 122 | value |= Wire.read(); 123 | value <<= 8; 124 | value |= Wire.read(); 125 | 126 | return value; 127 | } 128 | 129 | /**************************************************************************/ 130 | /*! 131 | @brief Reads the factory-set coefficients 132 | */ 133 | /**************************************************************************/ 134 | void BMP280::readCoefficients(void) 135 | { 136 | _bmp280_calib.dig_T1 = read16_LE(BMP280_REGISTER_DIG_T1); 137 | _bmp280_calib.dig_T2 = readS16_LE(BMP280_REGISTER_DIG_T2); 138 | _bmp280_calib.dig_T3 = readS16_LE(BMP280_REGISTER_DIG_T3); 139 | 140 | _bmp280_calib.dig_P1 = read16_LE(BMP280_REGISTER_DIG_P1); 141 | _bmp280_calib.dig_P2 = readS16_LE(BMP280_REGISTER_DIG_P2); 142 | _bmp280_calib.dig_P3 = readS16_LE(BMP280_REGISTER_DIG_P3); 143 | _bmp280_calib.dig_P4 = readS16_LE(BMP280_REGISTER_DIG_P4); 144 | _bmp280_calib.dig_P5 = readS16_LE(BMP280_REGISTER_DIG_P5); 145 | _bmp280_calib.dig_P6 = readS16_LE(BMP280_REGISTER_DIG_P6); 146 | _bmp280_calib.dig_P7 = readS16_LE(BMP280_REGISTER_DIG_P7); 147 | _bmp280_calib.dig_P8 = readS16_LE(BMP280_REGISTER_DIG_P8); 148 | _bmp280_calib.dig_P9 = readS16_LE(BMP280_REGISTER_DIG_P9); 149 | } 150 | 151 | /**************************************************************************/ 152 | /*! 153 | 154 | */ 155 | /**************************************************************************/ 156 | float BMP280::readTemperature(void) 157 | { 158 | int32_t var1, var2; 159 | 160 | int32_t adc_T = read24(BMP280_REGISTER_TEMPDATA); 161 | adc_T >>= 4; 162 | 163 | var1 = ((((adc_T>>3) - ((int32_t)_bmp280_calib.dig_T1 <<1))) * 164 | ((int32_t)_bmp280_calib.dig_T2)) >> 11; 165 | 166 | var2 = (((((adc_T>>4) - ((int32_t)_bmp280_calib.dig_T1)) * 167 | ((adc_T>>4) - ((int32_t)_bmp280_calib.dig_T1))) >> 12) * 168 | ((int32_t)_bmp280_calib.dig_T3)) >> 14; 169 | 170 | t_fine = var1 + var2; 171 | 172 | float T = (t_fine * 5 + 128) >> 8; 173 | return T/100; 174 | } 175 | 176 | /**************************************************************************/ 177 | /*! 178 | 179 | */ 180 | /**************************************************************************/ 181 | float BMP280::readPressure(void) { 182 | int64_t var1, var2, p; 183 | 184 | // Must be done first to get the t_fine variable set up 185 | readTemperature(); 186 | 187 | int32_t adc_P = read24(BMP280_REGISTER_PRESSUREDATA); 188 | adc_P >>= 4; 189 | 190 | var1 = ((int64_t)t_fine) - 128000; 191 | var2 = var1 * var1 * (int64_t)_bmp280_calib.dig_P6; 192 | var2 = var2 + ((var1*(int64_t)_bmp280_calib.dig_P5)<<17); 193 | var2 = var2 + (((int64_t)_bmp280_calib.dig_P4)<<35); 194 | var1 = ((var1 * var1 * (int64_t)_bmp280_calib.dig_P3)>>8) + 195 | ((var1 * (int64_t)_bmp280_calib.dig_P2)<<12); 196 | var1 = (((((int64_t)1)<<47)+var1))*((int64_t)_bmp280_calib.dig_P1)>>33; 197 | 198 | if (var1 == 0) { 199 | return 0; // avoid exception caused by division by zero 200 | } 201 | p = 1048576 - adc_P; 202 | p = (((p<<31) - var2)*3125) / var1; 203 | var1 = (((int64_t)_bmp280_calib.dig_P9) * (p>>13) * (p>>13)) >> 25; 204 | var2 = (((int64_t)_bmp280_calib.dig_P8) * p) >> 19; 205 | 206 | p = ((p + var1 + var2) >> 8) + (((int64_t)_bmp280_calib.dig_P7)<<4); 207 | return (float)p/256; 208 | } 209 | 210 | float BMP280::readAltitude(float seaLevelhPa) { 211 | float altitude; 212 | 213 | float pressure = readPressure(); // in Si units for Pascal 214 | pressure /= 100; 215 | 216 | altitude = 44330 * (1.0 - pow(pressure / seaLevelhPa, 0.1903)); 217 | 218 | return altitude; 219 | } 220 | -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/bmp280.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bmp280.ino 3 | * @brief Library for interfacing with the BMP280 4 | * (Based on library written by Adafruit) 5 | */ 6 | 7 | 8 | #ifndef BMP280_H 9 | #define BMP280_H 10 | 11 | #include "Arduino.h" 12 | 13 | /* I2C settings */ 14 | #define BMP280_ADDRESS (0x76) 15 | #define BMP280_CHIPID (0x58) 16 | 17 | /* List of registers */ 18 | enum 19 | { 20 | BMP280_REGISTER_DIG_T1 = 0x88, 21 | BMP280_REGISTER_DIG_T2 = 0x8A, 22 | BMP280_REGISTER_DIG_T3 = 0x8C, 23 | 24 | BMP280_REGISTER_DIG_P1 = 0x8E, 25 | BMP280_REGISTER_DIG_P2 = 0x90, 26 | BMP280_REGISTER_DIG_P3 = 0x92, 27 | BMP280_REGISTER_DIG_P4 = 0x94, 28 | BMP280_REGISTER_DIG_P5 = 0x96, 29 | BMP280_REGISTER_DIG_P6 = 0x98, 30 | BMP280_REGISTER_DIG_P7 = 0x9A, 31 | BMP280_REGISTER_DIG_P8 = 0x9C, 32 | BMP280_REGISTER_DIG_P9 = 0x9E, 33 | 34 | BMP280_REGISTER_CHIPID = 0xD0, 35 | BMP280_REGISTER_VERSION = 0xD1, 36 | BMP280_REGISTER_SOFTRESET = 0xE0, 37 | 38 | BMP280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 39 | 40 | BMP280_REGISTER_CONTROL = 0xF4, 41 | BMP280_REGISTER_CONFIG = 0xF5, 42 | BMP280_REGISTER_PRESSUREDATA = 0xF7, 43 | BMP280_REGISTER_TEMPDATA = 0xFA, 44 | }; 45 | 46 | /* calibration data struct */ 47 | typedef struct 48 | { 49 | uint16_t dig_T1; 50 | int16_t dig_T2; 51 | int16_t dig_T3; 52 | 53 | uint16_t dig_P1; 54 | int16_t dig_P2; 55 | int16_t dig_P3; 56 | int16_t dig_P4; 57 | int16_t dig_P5; 58 | int16_t dig_P6; 59 | int16_t dig_P7; 60 | int16_t dig_P8; 61 | int16_t dig_P9; 62 | 63 | uint8_t dig_H1; 64 | int16_t dig_H2; 65 | uint8_t dig_H3; 66 | int16_t dig_H4; 67 | int16_t dig_H5; 68 | int8_t dig_H6; 69 | } bmp280_calib_data; 70 | 71 | /* class definitions */ 72 | class BMP280 73 | { 74 | public: 75 | BMP280(); 76 | bool begin(uint8_t addr = BMP280_ADDRESS, uint8_t chipid = BMP280_CHIPID); 77 | float readTemperature(void); 78 | float readPressure(void); 79 | float readAltitude(float seaLevelhPa = 1013.25); 80 | 81 | private: 82 | 83 | void readCoefficients(void); 84 | 85 | void write8(byte reg, byte value); 86 | uint8_t read8(byte reg); 87 | uint16_t read16(byte reg); 88 | uint32_t read24(byte reg); 89 | int16_t readS16(byte reg); 90 | uint16_t read16_LE(byte reg); // little endian 91 | int16_t readS16_LE(byte reg); // little endian 92 | 93 | uint8_t _i2caddr; 94 | int32_t _sensorID; 95 | int32_t t_fine; 96 | 97 | bmp280_calib_data _bmp280_calib; 98 | }; 99 | 100 | #endif // BMP280 // 101 | -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/esp_10dof.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 XXXXXXXX 3 | * 4 | * This file is subject to the terms and conditions defined in file 'LICENSE.txt', 5 | * which is part of this source code package. 6 | */ 7 | 8 | /** 9 | * @file esp_10dof.h 10 | * @brief Main file 11 | */ 12 | 13 | #include 14 | #include 15 | #include "lsm9ds1.h" 16 | #include "bmp280.h" 17 | #include "imu.h" 18 | 19 | #define LED 2 20 | 21 | /* LSM9DS1 class instance */ 22 | LSM9DS1 imu; 23 | 24 | /* BMP280 class instance */ 25 | BMP280 bar; 26 | 27 | /* I2C address */ 28 | #define LSM9DS1_M 0x1E // I2C 7-bit address for magnetometer 29 | #define LSM9DS1_AG 0x6B // I2C 7-bit address for accelerometer/gyroscope 30 | 31 | /* timer interrupt settings */ 32 | #define PERIOD_MS 500 // timer interrupt period in ms 33 | 34 | /* macro to convert ms to esp ticks */ 35 | #define MS_TO_ESPTICK(x) ( x * 80000L ) // 80MHz == 1sec 36 | 37 | const uint32_t period_ms = MS_TO_ESPTICK(PERIOD_MS); 38 | 39 | bool tick_flag = false; 40 | uint8_t led_toggle = 0, led_counter = 0; 41 | float accel, gyro, mag, temp, pressure, altitude; 42 | 43 | /***************************************************************************** 44 | * Setup 45 | ****************************************************************************/ 46 | void setup() 47 | { 48 | pinMode(LED, OUTPUT); // initialize LED pin as output 49 | Serial.begin(115200); 50 | 51 | sensor_setup(); // configure sensors 52 | timer_setup(); // configure timer 53 | } 54 | 55 | /***************************************************************************** 56 | * Main Loop 57 | ****************************************************************************/ 58 | void loop() 59 | { 60 | if (tick_flag) { 61 | tick_flag = false; 62 | 63 | getAccel(0); 64 | getGyro(0); 65 | getMag(0); 66 | 67 | temp = bar.readTemperature(); 68 | pressure = bar.readPressure(); 69 | altitude = bar.readAltitude(1013.25); 70 | 71 | printData(); 72 | } 73 | } 74 | 75 | /***************************************************************************** 76 | * Interrupt Service Routines (ISR) 77 | ****************************************************************************/ 78 | 79 | /** 80 | * @brief Timer o interrupt service routine 81 | */ 82 | void timer0_ISR () 83 | { 84 | tick_flag = true; 85 | if (led_counter++ >= 1000/period_ms) { 86 | led_counter = 0; 87 | led_toggle = (led_toggle == 1) ? 0 : 1; 88 | digitalWrite(LED, led_toggle); 89 | } 90 | timer0_write(ESP.getCycleCount() + period_ms); 91 | } 92 | 93 | /***************************************************************************** 94 | * Private Functions 95 | ****************************************************************************/ 96 | 97 | /** 98 | * @brief Initializes on-board sensors 99 | * @return Nothing 100 | */ 101 | void sensor_setup() 102 | { 103 | /* initialize 9-dof imu */ 104 | // Before initializing the IMU, there are a few settings we may need to adjust. 105 | // Use the settings struct to set the device's communication mode and addresses: 106 | imu.settings.device.commInterface = IMU_MODE_I2C; 107 | imu.settings.device.mAddress = LSM9DS1_M; 108 | imu.settings.device.agAddress = LSM9DS1_AG; 109 | // The above lines will only take effect AFTER calling imu.begin(), which verifies 110 | // communication with the IMU and turns it on. 111 | if (!imu.begin()) 112 | { 113 | Serial.println("Failed to communicate with LSM9DS1."); 114 | while (1) 115 | ; 116 | } 117 | 118 | /* initialize barometer */ 119 | if (!bar.begin()) 120 | { 121 | Serial.println("Failed to communicate with BMP280."); 122 | while (1) 123 | ; 124 | } 125 | } 126 | 127 | /** 128 | * @brief Initializes on-board sensors 129 | * @return Nothing 130 | */ 131 | void timer_setup() 132 | { 133 | noInterrupts(); 134 | timer0_isr_init(); 135 | timer0_attachInterrupt(timer0_ISR); 136 | timer0_write(ESP.getCycleCount() + period_ms); 137 | interrupts(); 138 | } 139 | 140 | /** 141 | * @brief Print sensor data 142 | * @return Nothing 143 | */ 144 | void printData() 145 | { 146 | // print gyro 147 | Serial.print("Gyro: "); 148 | Serial.print(imu_gx, 2); 149 | Serial.print(", "); 150 | Serial.print(imu_gy, 2); 151 | Serial.print(", "); 152 | Serial.print(imu_gz, 2); 153 | Serial.println(" deg/s"); 154 | 155 | // print accel 156 | Serial.print("Accel: "); 157 | Serial.print(imu_ax, 2); 158 | Serial.print(", "); 159 | Serial.print(imu_ay, 2); 160 | Serial.print(", "); 161 | Serial.print(imu_az, 2); 162 | Serial.println(" g"); 163 | 164 | // print mag 165 | Serial.print("Accel: "); 166 | Serial.print(imu_mx, 2); 167 | Serial.print(", "); 168 | Serial.print(imu_my, 2); 169 | Serial.print(", "); 170 | Serial.print(imu_mz, 2); 171 | Serial.println(" gauss"); 172 | 173 | // print barometer 174 | Serial.print("Temperature: "); 175 | Serial.print(temp, 2); 176 | Serial.print(" *C, "); 177 | Serial.print("Pressure: "); 178 | Serial.print(pressure, 2); 179 | Serial.print(" Pa, "); 180 | Serial.print("Altitude: "); 181 | Serial.print(altitude, 2); 182 | Serial.println(" m, "); 183 | 184 | Serial.println(); 185 | } 186 | -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/imu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 XXXXXXXX 3 | * 4 | * This file is subject to the terms and conditions defined in file 'LICENSE.txt', 5 | * which is part of this source code package. 6 | */ 7 | 8 | /** 9 | * @file imu.h 10 | * @brief High level imu functions 11 | */ 12 | 13 | 14 | #ifndef IMU_H 15 | #define IMU_H 16 | 17 | #include "Arduino.h" 18 | 19 | // Earth's magnetic field varies by location. Add or subtract 20 | // a declination to get a more accurate heading. Calculate 21 | // your's here: 22 | // http://www.ngdc.noaa.gov/geomag-web/#declination 23 | #define DECLINATION -14.55 // Declination (degrees) in Edmoton, AB, Canada. 24 | 25 | float imu_gx, imu_gy, imu_gz; 26 | float imu_ax, imu_ay, imu_az; 27 | float imu_mx, imu_my, imu_mz; 28 | 29 | void getGyro(bool verbose = 0); 30 | void getAccel(bool verbose = 0); 31 | void getMag(bool verbose = 0); 32 | void getSimpleAttitude(float ax, float ay, float az, float mx, float my, float mz); 33 | 34 | #endif /* IMU_H */ -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/imu.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 XXXXXXXX 3 | * 4 | * This file is subject to the terms and conditions defined in file 'LICENSE.txt', 5 | * which is part of this source code package. 6 | */ 7 | 8 | /** 9 | * @file imu.ino 10 | * @brief High level imu functions 11 | */ 12 | 13 | 14 | #include 15 | #include 16 | #include "lsm9ds1.h" 17 | 18 | void getGyro(bool verbose) 19 | { 20 | // To read from the gyroscope, you must first call the readGyro() function. 21 | // When this exits, it'll update the gx, gy, and gz variables with the most current data. 22 | imu.readGyro(); 23 | 24 | // Use the calcGyro helper function to convert a raw ADC value to deg/s (º/s) 25 | imu_gx = imu.calcGyro(imu.gx); 26 | imu_gy = imu.calcGyro(imu.gy); 27 | imu_gz = imu.calcGyro(imu.gz); 28 | 29 | if (verbose) { 30 | // If verbose is true, print data 31 | Serial.print("Gyro: "); 32 | Serial.print(imu_gx, 2); 33 | Serial.print(", "); 34 | Serial.print(imu_gy, 2); 35 | Serial.print(", "); 36 | Serial.print(imu_gz, 2); 37 | Serial.println(" deg/s"); 38 | } 39 | } 40 | 41 | void getAccel(bool verbose) 42 | { 43 | // To read from the accelerometer, you must first call the readAccel() function. 44 | // When this exits, it'll update the ax, ay, and az variables with the most current data. 45 | imu.readAccel(); 46 | 47 | // Use the calcAccel helper function to convert a raw ADC value to g's 48 | imu_ax = imu.calcAccel(imu.ax); 49 | imu_ay = imu.calcAccel(imu.ay); 50 | imu_az = imu.calcAccel(imu.az); 51 | 52 | if (verbose) { 53 | // If verbose is true, print data 54 | Serial.print("Accel: "); 55 | Serial.print(imu_ax, 2); 56 | Serial.print(", "); 57 | Serial.print(imu_ay, 2); 58 | Serial.print(", "); 59 | Serial.print(imu_az, 2); 60 | Serial.println(" g"); 61 | } 62 | } 63 | 64 | void getMag(bool verbose) 65 | { 66 | // To read from the magnetometer, you must first call the readMag() function. 67 | // When this exits, it'll update the mx, my, and mz variables with the most current data. 68 | imu.readMag(); 69 | 70 | // Use the calcMag helper function to convert a raw ADC value to Gauss 71 | imu_mx = imu.calcMag(imu.mx); 72 | imu_my = imu.calcMag(imu.my); 73 | imu_mz = imu.calcMag(imu.mz); 74 | 75 | if (verbose) { 76 | // If verbose is true, print data 77 | Serial.print("Accel: "); 78 | Serial.print(imu_mx, 2); 79 | Serial.print(", "); 80 | Serial.print(imu_my, 2); 81 | Serial.print(", "); 82 | Serial.print(imu_mz, 2); 83 | Serial.println(" gauss"); 84 | } 85 | } 86 | 87 | // Calculate pitch, roll, and heading. 88 | // Pitch/roll calculations take from this app note: 89 | // http://cache.freescale.com/files/sensors/doc/app_note/AN3461.pdf?fpsp=1 90 | // Heading calculations taken from this app note: 91 | // http://www51.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/Magnetic__Literature_Application_notes-documents/AN203_Compass_Heading_Using_Magnetometers.pdf 92 | void getSimpleAttitude(float ax, float ay, float az, float mx, float my, float mz) 93 | { 94 | float roll = atan2(ay, az); 95 | float pitch = atan2(-ax, sqrt(ay * ay + az * az)); 96 | 97 | float heading; 98 | if (my == 0) 99 | heading = (mx < 0) ? 180.0 : 0; 100 | else 101 | heading = atan2(mx, my); 102 | 103 | heading -= DECLINATION * PI / 180; 104 | 105 | if (heading > PI) heading -= (2 * PI); 106 | else if (heading < -PI) heading += (2 * PI); 107 | else if (heading < 0) heading += 2 * PI; 108 | 109 | // Convert everything from radians to degrees: 110 | heading *= 180.0 / PI; 111 | pitch *= 180.0 / PI; 112 | roll *= 180.0 / PI; 113 | 114 | Serial.print("Pitch, Roll: "); 115 | Serial.print(pitch, 2); 116 | Serial.print(", "); 117 | Serial.println(roll, 2); 118 | Serial.print("Heading: "); Serial.println(heading, 2); 119 | } 120 | -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/lsm9ds1.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lsm9ds1.ino 3 | * @brief Library for interfacing with the LSM9DS1 4 | * (Based on library written by sparkfun) 5 | */ 6 | 7 | 8 | #ifndef LSM9DS1_H 9 | #define LSM9DS1_H 10 | 11 | #include "Arduino.h" 12 | 13 | #include "lsm9ds1_reg.h" 14 | #include "lsm9ds1_types.h" 15 | 16 | #define LSM9DS1_AG_ADDR(sa0) ((sa0) == 0 ? 0x6A : 0x6B) 17 | #define LSM9DS1_M_ADDR(sa1) ((sa1) == 0 ? 0x1C : 0x1E) 18 | 19 | enum lsm9ds1_axis { 20 | X_AXIS, 21 | Y_AXIS, 22 | Z_AXIS, 23 | ALL_AXIS 24 | }; 25 | 26 | class LSM9DS1 27 | { 28 | public: 29 | IMUSettings settings; 30 | 31 | // We'll store the gyro, accel, and magnetometer readings in a series of 32 | // public class variables. Each sensor gets three variables -- one for each 33 | // axis. Call readGyro(), readAccel(), and readMag() first, before using 34 | // these variables! 35 | // These values are the RAW signed 16-bit readings from the sensors. 36 | int16_t gx, gy, gz; // x, y, and z axis readings of the gyroscope 37 | int16_t ax, ay, az; // x, y, and z axis readings of the accelerometer 38 | int16_t mx, my, mz; // x, y, and z axis readings of the magnetometer 39 | int16_t temperature; // Chip temperature 40 | float gBias[3], aBias[3], mBias[3]; 41 | int16_t gBiasRaw[3], aBiasRaw[3], mBiasRaw[3]; 42 | 43 | // LSM9DS1 -- LSM9DS1 class constructor 44 | // The constructor will set up a handful of private variables, and set the 45 | // communication mode as well. 46 | // Input: 47 | // - interface = Either IMU_MODE_SPI or IMU_MODE_I2C, whichever you're using 48 | // to talk to the IC. 49 | // - xgAddr = If IMU_MODE_I2C, this is the I2C address of the accel/gyroscope. 50 | // If IMU_MODE_SPI, this is the chip select pin of the gyro (CS_AG) 51 | // - mAddr = If IMU_MODE_I2C, this is the I2C address of the magnetometer. 52 | // If IMU_MODE_SPI, this is the cs pin of the magnetometer (CS_M) 53 | LSM9DS1(interface_mode interface, uint8_t xgAddr, uint8_t mAddr); 54 | LSM9DS1(); 55 | 56 | // begin() -- Initialize the gyro, accelerometer, and magnetometer. 57 | // This will set up the scale and output rate of each sensor. The values set 58 | // in the IMUSettings struct will take effect after calling this function. 59 | uint16_t begin(); 60 | 61 | void calibrate(bool autoCalc = true); 62 | void calibrateMag(bool loadIn = true); 63 | void magOffset(uint8_t axis, int16_t offset); 64 | 65 | // accelAvailable() -- Polls the accelerometer status register to check 66 | // if new data is available. 67 | // Output: 1 - New data available 68 | // 0 - No new data available 69 | uint8_t accelAvailable(); 70 | 71 | // gyroAvailable() -- Polls the gyroscope status register to check 72 | // if new data is available. 73 | // Output: 1 - New data available 74 | // 0 - No new data available 75 | uint8_t gyroAvailable(); 76 | 77 | // gyroAvailable() -- Polls the temperature status register to check 78 | // if new data is available. 79 | // Output: 1 - New data available 80 | // 0 - No new data available 81 | uint8_t tempAvailable(); 82 | 83 | // magAvailable() -- Polls the accelerometer status register to check 84 | // if new data is available. 85 | // Input: 86 | // - axis can be either X_AXIS, Y_AXIS, Z_AXIS, to check for new data 87 | // on one specific axis. Or ALL_AXIS (default) to check for new data 88 | // on all axes. 89 | // Output: 1 - New data available 90 | // 0 - No new data available 91 | uint8_t magAvailable(lsm9ds1_axis axis = ALL_AXIS); 92 | 93 | // readGyro() -- Read the gyroscope output registers. 94 | // This function will read all six gyroscope output registers. 95 | // The readings are stored in the class' gx, gy, and gz variables. Read 96 | // those _after_ calling readGyro(). 97 | void readGyro(); 98 | 99 | // int16_t readGyro(axis) -- Read a specific axis of the gyroscope. 100 | // [axis] can be any of X_AXIS, Y_AXIS, or Z_AXIS. 101 | // Input: 102 | // - axis: can be either X_AXIS, Y_AXIS, or Z_AXIS. 103 | // Output: 104 | // A 16-bit signed integer with sensor data on requested axis. 105 | int16_t readGyro(lsm9ds1_axis axis); 106 | 107 | // readAccel() -- Read the accelerometer output registers. 108 | // This function will read all six accelerometer output registers. 109 | // The readings are stored in the class' ax, ay, and az variables. Read 110 | // those _after_ calling readAccel(). 111 | void readAccel(); 112 | 113 | // int16_t readAccel(axis) -- Read a specific axis of the accelerometer. 114 | // [axis] can be any of X_AXIS, Y_AXIS, or Z_AXIS. 115 | // Input: 116 | // - axis: can be either X_AXIS, Y_AXIS, or Z_AXIS. 117 | // Output: 118 | // A 16-bit signed integer with sensor data on requested axis. 119 | int16_t readAccel(lsm9ds1_axis axis); 120 | 121 | // readMag() -- Read the magnetometer output registers. 122 | // This function will read all six magnetometer output registers. 123 | // The readings are stored in the class' mx, my, and mz variables. Read 124 | // those _after_ calling readMag(). 125 | void readMag(); 126 | 127 | // int16_t readMag(axis) -- Read a specific axis of the magnetometer. 128 | // [axis] can be any of X_AXIS, Y_AXIS, or Z_AXIS. 129 | // Input: 130 | // - axis: can be either X_AXIS, Y_AXIS, or Z_AXIS. 131 | // Output: 132 | // A 16-bit signed integer with sensor data on requested axis. 133 | int16_t readMag(lsm9ds1_axis axis); 134 | 135 | // readTemp() -- Read the temperature output register. 136 | // This function will read two temperature output registers. 137 | // The combined readings are stored in the class' temperature variables. Read 138 | // those _after_ calling readTemp(). 139 | void readTemp(); 140 | 141 | // calcGyro() -- Convert from RAW signed 16-bit value to degrees per second 142 | // This function reads in a signed 16-bit value and returns the scaled 143 | // DPS. This function relies on gScale and gRes being correct. 144 | // Input: 145 | // - gyro = A signed 16-bit raw reading from the gyroscope. 146 | float calcGyro(int16_t gyro); 147 | 148 | // calcAccel() -- Convert from RAW signed 16-bit value to gravity (g's). 149 | // This function reads in a signed 16-bit value and returns the scaled 150 | // g's. This function relies on aScale and aRes being correct. 151 | // Input: 152 | // - accel = A signed 16-bit raw reading from the accelerometer. 153 | float calcAccel(int16_t accel); 154 | 155 | // calcMag() -- Convert from RAW signed 16-bit value to Gauss (Gs) 156 | // This function reads in a signed 16-bit value and returns the scaled 157 | // Gs. This function relies on mScale and mRes being correct. 158 | // Input: 159 | // - mag = A signed 16-bit raw reading from the magnetometer. 160 | float calcMag(int16_t mag); 161 | 162 | // setGyroScale() -- Set the full-scale range of the gyroscope. 163 | // This function can be called to set the scale of the gyroscope to 164 | // 245, 500, or 200 degrees per second. 165 | // Input: 166 | // - gScl = The desired gyroscope scale. Must be one of three possible 167 | // values from the gyro_scale. 168 | void setGyroScale(uint16_t gScl); 169 | 170 | // setAccelScale() -- Set the full-scale range of the accelerometer. 171 | // This function can be called to set the scale of the accelerometer to 172 | // 2, 4, 6, 8, or 16 g's. 173 | // Input: 174 | // - aScl = The desired accelerometer scale. Must be one of five possible 175 | // values from the accel_scale. 176 | void setAccelScale(uint8_t aScl); 177 | 178 | // setMagScale() -- Set the full-scale range of the magnetometer. 179 | // This function can be called to set the scale of the magnetometer to 180 | // 2, 4, 8, or 12 Gs. 181 | // Input: 182 | // - mScl = The desired magnetometer scale. Must be one of four possible 183 | // values from the mag_scale. 184 | void setMagScale(uint8_t mScl); 185 | 186 | // setGyroODR() -- Set the output data rate and bandwidth of the gyroscope 187 | // Input: 188 | // - gRate = The desired output rate and cutoff frequency of the gyro. 189 | void setGyroODR(uint8_t gRate); 190 | 191 | // setAccelODR() -- Set the output data rate of the accelerometer 192 | // Input: 193 | // - aRate = The desired output rate of the accel. 194 | void setAccelODR(uint8_t aRate); 195 | 196 | // setMagODR() -- Set the output data rate of the magnetometer 197 | // Input: 198 | // - mRate = The desired output rate of the mag. 199 | void setMagODR(uint8_t mRate); 200 | 201 | // configInactivity() -- Configure inactivity interrupt parameters 202 | // Input: 203 | // - duration = Inactivity duration - actual value depends on gyro ODR 204 | // - threshold = Activity Threshold 205 | // - sleepOn = Gyroscope operating mode during inactivity. 206 | // true: gyroscope in sleep mode 207 | // false: gyroscope in power-down 208 | void configInactivity(uint8_t duration, uint8_t threshold, bool sleepOn); 209 | 210 | // configAccelInt() -- Configure Accelerometer Interrupt Generator 211 | // Input: 212 | // - generator = Interrupt axis/high-low events 213 | // Any OR'd combination of ZHIE_XL, ZLIE_XL, YHIE_XL, YLIE_XL, XHIE_XL, XLIE_XL 214 | // - andInterrupts = AND/OR combination of interrupt events 215 | // true: AND combination 216 | // false: OR combination 217 | void configAccelInt(uint8_t generator, bool andInterrupts = false); 218 | 219 | // configAccelThs() -- Configure the threshold of an accelereomter axis 220 | // Input: 221 | // - threshold = Interrupt threshold. Possible values: 0-255. 222 | // Multiply by 128 to get the actual raw accel value. 223 | // - axis = Axis to be configured. Either X_AXIS, Y_AXIS, or Z_AXIS 224 | // - duration = Duration value must be above or below threshold to trigger interrupt 225 | // - wait = Wait function on duration counter 226 | // true: Wait for duration samples before exiting interrupt 227 | // false: Wait function off 228 | void configAccelThs(uint8_t threshold, lsm9ds1_axis axis, uint8_t duration = 0, bool wait = 0); 229 | 230 | // configGyroInt() -- Configure Gyroscope Interrupt Generator 231 | // Input: 232 | // - generator = Interrupt axis/high-low events 233 | // Any OR'd combination of ZHIE_G, ZLIE_G, YHIE_G, YLIE_G, XHIE_G, XLIE_G 234 | // - aoi = AND/OR combination of interrupt events 235 | // true: AND combination 236 | // false: OR combination 237 | // - latch: latch gyroscope interrupt request. 238 | void configGyroInt(uint8_t generator, bool aoi, bool latch); 239 | 240 | // configGyroThs() -- Configure the threshold of a gyroscope axis 241 | // Input: 242 | // - threshold = Interrupt threshold. Possible values: 0-0x7FF. 243 | // Value is equivalent to raw gyroscope value. 244 | // - axis = Axis to be configured. Either X_AXIS, Y_AXIS, or Z_AXIS 245 | // - duration = Duration value must be above or below threshold to trigger interrupt 246 | // - wait = Wait function on duration counter 247 | // true: Wait for duration samples before exiting interrupt 248 | // false: Wait function off 249 | void configGyroThs(int16_t threshold, lsm9ds1_axis axis, uint8_t duration, bool wait); 250 | 251 | // configInt() -- Configure INT1 or INT2 (Gyro and Accel Interrupts only) 252 | // Input: 253 | // - interrupt = Select INT1 or INT2 254 | // Possible values: XG_INT1 or XG_INT2 255 | // - generator = Or'd combination of interrupt generators. 256 | // Possible values: INT_DRDY_XL, INT_DRDY_G, INT1_BOOT (INT1 only), INT2_DRDY_TEMP (INT2 only) 257 | // INT_FTH, INT_OVR, INT_FSS5, INT_IG_XL (INT1 only), INT1_IG_G (INT1 only), INT2_INACT (INT2 only) 258 | // - activeLow = Interrupt active configuration 259 | // Can be either INT_ACTIVE_HIGH or INT_ACTIVE_LOW 260 | // - pushPull = Push-pull or open drain interrupt configuration 261 | // Can be either INT_PUSH_PULL or INT_OPEN_DRAIN 262 | void configInt(interrupt_select interupt, uint8_t generator, 263 | h_lactive activeLow = INT_ACTIVE_LOW, pp_od pushPull = INT_PUSH_PULL); 264 | 265 | // configMagInt() -- Configure Magnetometer Interrupt Generator 266 | // Input: 267 | // - generator = Interrupt axis/high-low events 268 | // Any OR'd combination of ZIEN, YIEN, XIEN 269 | // - activeLow = Interrupt active configuration 270 | // Can be either INT_ACTIVE_HIGH or INT_ACTIVE_LOW 271 | // - latch: latch gyroscope interrupt request. 272 | void configMagInt(uint8_t generator, h_lactive activeLow, bool latch = true); 273 | 274 | // configMagThs() -- Configure the threshold of a gyroscope axis 275 | // Input: 276 | // - threshold = Interrupt threshold. Possible values: 0-0x7FF. 277 | // Value is equivalent to raw magnetometer value. 278 | void configMagThs(uint16_t threshold); 279 | 280 | // getGyroIntSrc() -- Get contents of Gyroscope interrupt source register 281 | uint8_t getGyroIntSrc(); 282 | 283 | // getGyroIntSrc() -- Get contents of accelerometer interrupt source register 284 | uint8_t getAccelIntSrc(); 285 | 286 | // getGyroIntSrc() -- Get contents of magnetometer interrupt source register 287 | uint8_t getMagIntSrc(); 288 | 289 | // getGyroIntSrc() -- Get status of inactivity interrupt 290 | uint8_t getInactivity(); 291 | 292 | // sleepGyro() -- Sleep or wake the gyroscope 293 | // Input: 294 | // - enable: True = sleep gyro. False = wake gyro. 295 | void sleepGyro(bool enable = true); 296 | 297 | // enableFIFO() - Enable or disable the FIFO 298 | // Input: 299 | // - enable: true = enable, false = disable. 300 | void enableFIFO(bool enable = true); 301 | 302 | // setFIFO() - Configure FIFO mode and Threshold 303 | // Input: 304 | // - fifoMode: Set FIFO mode to off, FIFO (stop when full), continuous, bypass 305 | // Possible inputs: FIFO_OFF, FIFO_THS, FIFO_CONT_TRIGGER, FIFO_OFF_TRIGGER, FIFO_CONT 306 | // - fifoThs: FIFO threshold level setting 307 | // Any value from 0-0x1F is acceptable. 308 | void setFIFO(fifoMode_type fifoMode, uint8_t fifoThs); 309 | 310 | // getFIFOSamples() - Get number of FIFO samples 311 | uint8_t getFIFOSamples(); 312 | 313 | 314 | protected: 315 | // x_mAddress and gAddress store the I2C address or SPI chip select pin 316 | // for each sensor. 317 | uint8_t _mAddress, _xgAddress; 318 | 319 | // gRes, aRes, and mRes store the current resolution for each sensor. 320 | // Units of these values would be DPS (or g's or Gs's) per ADC tick. 321 | // This value is calculated as (sensor scale) / (2^15). 322 | float gRes, aRes, mRes; 323 | 324 | // _autoCalc keeps track of whether we're automatically subtracting off 325 | // accelerometer and gyroscope bias calculated in calibrate(). 326 | bool _autoCalc; 327 | 328 | // init() -- Sets up gyro, accel, and mag settings to default. 329 | // - interface - Sets the interface mode (IMU_MODE_I2C or IMU_MODE_SPI) 330 | // - xgAddr - Sets either the I2C address of the accel/gyro or SPI chip 331 | // select pin connected to the CS_XG pin. 332 | // - mAddr - Sets either the I2C address of the magnetometer or SPI chip 333 | // select pin connected to the CS_M pin. 334 | void init(interface_mode interface, uint8_t xgAddr, uint8_t mAddr); 335 | 336 | // initGyro() -- Sets up the gyroscope to begin reading. 337 | // This function steps through all five gyroscope control registers. 338 | // Upon exit, the following parameters will be set: 339 | // - CTRL_REG1_G = 0x0F: Normal operation mode, all axes enabled. 340 | // 95 Hz ODR, 12.5 Hz cutoff frequency. 341 | // - CTRL_REG2_G = 0x00: HPF set to normal mode, cutoff frequency 342 | // set to 7.2 Hz (depends on ODR). 343 | // - CTRL_REG3_G = 0x88: Interrupt enabled on INT_G (set to push-pull and 344 | // active high). Data-ready output enabled on DRDY_G. 345 | // - CTRL_REG4_G = 0x00: Continuous update mode. Data LSB stored in lower 346 | // address. Scale set to 245 DPS. SPI mode set to 4-wire. 347 | // - CTRL_REG5_G = 0x00: FIFO disabled. HPF disabled. 348 | void initGyro(); 349 | 350 | // initAccel() -- Sets up the accelerometer to begin reading. 351 | // This function steps through all accelerometer related control registers. 352 | // Upon exit these registers will be set as: 353 | // - CTRL_REG0_XM = 0x00: FIFO disabled. HPF bypassed. Normal mode. 354 | // - CTRL_REG1_XM = 0x57: 100 Hz data rate. Continuous update. 355 | // all axes enabled. 356 | // - CTRL_REG2_XM = 0x00: 2g scale. 773 Hz anti-alias filter BW. 357 | // - CTRL_REG3_XM = 0x04: Accel data ready signal on INT1_XM pin. 358 | void initAccel(); 359 | 360 | // initMag() -- Sets up the magnetometer to begin reading. 361 | // This function steps through all magnetometer-related control registers. 362 | // Upon exit these registers will be set as: 363 | // - CTRL_REG4_XM = 0x04: Mag data ready signal on INT2_XM pin. 364 | // - CTRL_REG5_XM = 0x14: 100 Hz update rate. Low resolution. Interrupt 365 | // requests don't latch. Temperature sensor disabled. 366 | // - CTRL_REG6_XM = 0x00: 2 Gs scale. 367 | // - CTRL_REG7_XM = 0x00: Continuous conversion mode. Normal HPF mode. 368 | // - INT_CTRL_REG_M = 0x09: Interrupt active-high. Enable interrupts. 369 | void initMag(); 370 | 371 | // gReadByte() -- Reads a byte from a specified gyroscope register. 372 | // Input: 373 | // - subAddress = Register to be read from. 374 | // Output: 375 | // - An 8-bit value read from the requested address. 376 | uint8_t mReadByte(uint8_t subAddress); 377 | 378 | // gReadBytes() -- Reads a number of bytes -- beginning at an address 379 | // and incrementing from there -- from the gyroscope. 380 | // Input: 381 | // - subAddress = Register to be read from. 382 | // - * dest = A pointer to an array of uint8_t's. Values read will be 383 | // stored in here on return. 384 | // - count = The number of bytes to be read. 385 | // Output: No value is returned, but the `dest` array will store 386 | // the data read upon exit. 387 | void mReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count); 388 | 389 | // gWriteByte() -- Write a byte to a register in the gyroscope. 390 | // Input: 391 | // - subAddress = Register to be written to. 392 | // - data = data to be written to the register. 393 | void mWriteByte(uint8_t subAddress, uint8_t data); 394 | 395 | // xmReadByte() -- Read a byte from a register in the accel/mag sensor 396 | // Input: 397 | // - subAddress = Register to be read from. 398 | // Output: 399 | // - An 8-bit value read from the requested register. 400 | uint8_t xgReadByte(uint8_t subAddress); 401 | 402 | // xmReadBytes() -- Reads a number of bytes -- beginning at an address 403 | // and incrementing from there -- from the accelerometer/magnetometer. 404 | // Input: 405 | // - subAddress = Register to be read from. 406 | // - * dest = A pointer to an array of uint8_t's. Values read will be 407 | // stored in here on return. 408 | // - count = The number of bytes to be read. 409 | // Output: No value is returned, but the `dest` array will store 410 | // the data read upon exit. 411 | void xgReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count); 412 | 413 | // xmWriteByte() -- Write a byte to a register in the accel/mag sensor. 414 | // Input: 415 | // - subAddress = Register to be written to. 416 | // - data = data to be written to the register. 417 | void xgWriteByte(uint8_t subAddress, uint8_t data); 418 | 419 | // calcgRes() -- Calculate the resolution of the gyroscope. 420 | // This function will set the value of the gRes variable. gScale must 421 | // be set prior to calling this function. 422 | void calcgRes(); 423 | 424 | // calcmRes() -- Calculate the resolution of the magnetometer. 425 | // This function will set the value of the mRes variable. mScale must 426 | // be set prior to calling this function. 427 | void calcmRes(); 428 | 429 | // calcaRes() -- Calculate the resolution of the accelerometer. 430 | // This function will set the value of the aRes variable. aScale must 431 | // be set prior to calling this function. 432 | void calcaRes(); 433 | 434 | ////////////////////// 435 | // Helper Functions // 436 | ////////////////////// 437 | void constrainScales(); 438 | 439 | /////////////////// 440 | // SPI Functions // 441 | /////////////////// 442 | // initSPI() -- Initialize the SPI hardware. 443 | // This function will setup all SPI pins and related hardware. 444 | void initSPI(); 445 | 446 | // SPIwriteByte() -- Write a byte out of SPI to a register in the device 447 | // Input: 448 | // - csPin = The chip select pin of the slave device. 449 | // - subAddress = The register to be written to. 450 | // - data = Byte to be written to the register. 451 | void SPIwriteByte(uint8_t csPin, uint8_t subAddress, uint8_t data); 452 | 453 | // SPIreadByte() -- Read a single byte from a register over SPI. 454 | // Input: 455 | // - csPin = The chip select pin of the slave device. 456 | // - subAddress = The register to be read from. 457 | // Output: 458 | // - The byte read from the requested address. 459 | uint8_t SPIreadByte(uint8_t csPin, uint8_t subAddress); 460 | 461 | // SPIreadBytes() -- Read a series of bytes, starting at a register via SPI 462 | // Input: 463 | // - csPin = The chip select pin of a slave device. 464 | // - subAddress = The register to begin reading. 465 | // - * dest = Pointer to an array where we'll store the readings. 466 | // - count = Number of registers to be read. 467 | // Output: No value is returned by the function, but the registers read are 468 | // all stored in the *dest array given. 469 | void SPIreadBytes(uint8_t csPin, uint8_t subAddress, 470 | uint8_t * dest, uint8_t count); 471 | 472 | /////////////////// 473 | // I2C Functions // 474 | /////////////////// 475 | // initI2C() -- Initialize the I2C hardware. 476 | // This function will setup all I2C pins and related hardware. 477 | void initI2C(); 478 | 479 | // I2CwriteByte() -- Write a byte out of I2C to a register in the device 480 | // Input: 481 | // - address = The 7-bit I2C address of the slave device. 482 | // - subAddress = The register to be written to. 483 | // - data = Byte to be written to the register. 484 | void I2CwriteByte(uint8_t address, uint8_t subAddress, uint8_t data); 485 | 486 | // I2CreadByte() -- Read a single byte from a register over I2C. 487 | // Input: 488 | // - address = The 7-bit I2C address of the slave device. 489 | // - subAddress = The register to be read from. 490 | // Output: 491 | // - The byte read from the requested address. 492 | uint8_t I2CreadByte(uint8_t address, uint8_t subAddress); 493 | 494 | // I2CreadBytes() -- Read a series of bytes, starting at a register via SPI 495 | // Input: 496 | // - address = The 7-bit I2C address of the slave device. 497 | // - subAddress = The register to begin reading. 498 | // - * dest = Pointer to an array where we'll store the readings. 499 | // - count = Number of registers to be read. 500 | // Output: No value is returned by the function, but the registers read are 501 | // all stored in the *dest array given. 502 | uint8_t I2CreadBytes(uint8_t address, uint8_t subAddress, uint8_t * dest, uint8_t count); 503 | }; 504 | 505 | #endif // LSM9DS1_H // 506 | -------------------------------------------------------------------------------- /firmware-arduino/esp_10dof/lsm9ds1.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lsm9ds1.ino 3 | * @brief Library for interfacing with the LSM9DS1 4 | * (Based on library written by sparkfun) 5 | */ 6 | 7 | 8 | #include "lsm9ds1.h" 9 | #include "lsm9ds1_reg.h" 10 | #include "lsm9ds1_types.h" 11 | #include 12 | #include 13 | 14 | #define LSM9DS1_COMMUNICATION_TIMEOUT 1000 15 | 16 | float magSensitivity[4] = {0.00014, 0.00029, 0.00043, 0.00058}; 17 | 18 | LSM9DS1::LSM9DS1() 19 | { 20 | init(IMU_MODE_I2C, LSM9DS1_AG_ADDR(1), LSM9DS1_M_ADDR(1)); 21 | } 22 | 23 | LSM9DS1::LSM9DS1(interface_mode interface, uint8_t xgAddr, uint8_t mAddr) 24 | { 25 | init(interface, xgAddr, mAddr); 26 | } 27 | 28 | void LSM9DS1::init(interface_mode interface, uint8_t xgAddr, uint8_t mAddr) 29 | { 30 | settings.device.commInterface = interface; 31 | settings.device.agAddress = xgAddr; 32 | settings.device.mAddress = mAddr; 33 | 34 | settings.gyro.enabled = true; 35 | settings.gyro.enableX = true; 36 | settings.gyro.enableY = true; 37 | settings.gyro.enableZ = true; 38 | // gyro scale can be 245, 500, or 2000 39 | settings.gyro.scale = 245; 40 | // gyro sample rate: value between 1-6 41 | // 1 = 14.9 4 = 238 42 | // 2 = 59.5 5 = 476 43 | // 3 = 119 6 = 952 44 | settings.gyro.sampleRate = 6; 45 | // gyro cutoff frequency: value between 0-3 46 | // Actual value of cutoff frequency depends 47 | // on sample rate. 48 | settings.gyro.bandwidth = 0; 49 | settings.gyro.lowPowerEnable = false; 50 | settings.gyro.HPFEnable = false; 51 | // Gyro HPF cutoff frequency: value between 0-9 52 | // Actual value depends on sample rate. Only applies 53 | // if gyroHPFEnable is true. 54 | settings.gyro.HPFCutoff = 0; 55 | settings.gyro.flipX = false; 56 | settings.gyro.flipY = false; 57 | settings.gyro.flipZ = false; 58 | settings.gyro.orientation = 0; 59 | settings.gyro.latchInterrupt = true; 60 | 61 | settings.accel.enabled = true; 62 | settings.accel.enableX = true; 63 | settings.accel.enableY = true; 64 | settings.accel.enableZ = true; 65 | // accel scale can be 2, 4, 8, or 16 66 | settings.accel.scale = 2; 67 | // accel sample rate can be 1-6 68 | // 1 = 10 Hz 4 = 238 Hz 69 | // 2 = 50 Hz 5 = 476 Hz 70 | // 3 = 119 Hz 6 = 952 Hz 71 | settings.accel.sampleRate = 6; 72 | // Accel cutoff freqeuncy can be any value between -1 - 3. 73 | // -1 = bandwidth determined by sample rate 74 | // 0 = 408 Hz 2 = 105 Hz 75 | // 1 = 211 Hz 3 = 50 Hz 76 | settings.accel.bandwidth = -1; 77 | settings.accel.highResEnable = false; 78 | // accelHighResBandwidth can be any value between 0-3 79 | // LP cutoff is set to a factor of sample rate 80 | // 0 = ODR/50 2 = ODR/9 81 | // 1 = ODR/100 3 = ODR/400 82 | settings.accel.highResBandwidth = 0; 83 | 84 | settings.mag.enabled = true; 85 | // mag scale can be 4, 8, 12, or 16 86 | settings.mag.scale = 4; 87 | // mag data rate can be 0-7 88 | // 0 = 0.625 Hz 4 = 10 Hz 89 | // 1 = 1.25 Hz 5 = 20 Hz 90 | // 2 = 2.5 Hz 6 = 40 Hz 91 | // 3 = 5 Hz 7 = 80 Hz 92 | settings.mag.sampleRate = 7; 93 | settings.mag.tempCompensationEnable = false; 94 | // magPerformance can be any value between 0-3 95 | // 0 = Low power mode 2 = high performance 96 | // 1 = medium performance 3 = ultra-high performance 97 | settings.mag.XYPerformance = 3; 98 | settings.mag.ZPerformance = 3; 99 | settings.mag.lowPowerEnable = false; 100 | // magOperatingMode can be 0-2 101 | // 0 = continuous conversion 102 | // 1 = single-conversion 103 | // 2 = power down 104 | settings.mag.operatingMode = 0; 105 | 106 | settings.temp.enabled = true; 107 | for (int i=0; i<3; i++) 108 | { 109 | gBias[i] = 0; 110 | aBias[i] = 0; 111 | mBias[i] = 0; 112 | gBiasRaw[i] = 0; 113 | aBiasRaw[i] = 0; 114 | mBiasRaw[i] = 0; 115 | } 116 | _autoCalc = false; 117 | } 118 | 119 | 120 | uint16_t LSM9DS1::begin() 121 | { 122 | //! Todo: don't use _xgAddress or _mAddress, duplicating memory 123 | _xgAddress = settings.device.agAddress; 124 | _mAddress = settings.device.mAddress; 125 | 126 | constrainScales(); 127 | // Once we have the scale values, we can calculate the resolution 128 | // of each sensor. That's what these functions are for. One for each sensor 129 | calcgRes(); // Calculate DPS / ADC tick, stored in gRes variable 130 | calcmRes(); // Calculate Gs / ADC tick, stored in mRes variable 131 | calcaRes(); // Calculate g / ADC tick, stored in aRes variable 132 | 133 | // Now, initialize our hardware interface. 134 | if (settings.device.commInterface == IMU_MODE_I2C) // If we're using I2C 135 | initI2C(); // Initialize I2C 136 | else if (settings.device.commInterface == IMU_MODE_SPI) // else, if we're using SPI 137 | initSPI(); // Initialize SPI 138 | 139 | // To verify communication, we can read from the WHO_AM_I register of 140 | // each device. Store those in a variable so we can return them. 141 | uint8_t mTest = mReadByte(WHO_AM_I_M); // Read the gyro WHO_AM_I 142 | uint8_t xgTest = xgReadByte(WHO_AM_I_XG); // Read the accel/mag WHO_AM_I 143 | uint16_t whoAmICombined = (xgTest << 8) | mTest; 144 | 145 | if (whoAmICombined != ((WHO_AM_I_AG_RSP << 8) | WHO_AM_I_M_RSP)) 146 | return 0; 147 | 148 | // Gyro initialization stuff: 149 | initGyro(); // This will "turn on" the gyro. Setting up interrupts, etc. 150 | 151 | // Accelerometer initialization stuff: 152 | initAccel(); // "Turn on" all axes of the accel. Set up interrupts, etc. 153 | 154 | // Magnetometer initialization stuff: 155 | initMag(); // "Turn on" all axes of the mag. Set up interrupts, etc. 156 | 157 | // Once everything is initialized, return the WHO_AM_I registers we read: 158 | return whoAmICombined; 159 | } 160 | 161 | void LSM9DS1::initGyro() 162 | { 163 | uint8_t tempRegValue = 0; 164 | 165 | // CTRL_REG1_G (Default value: 0x00) 166 | // [ODR_G2][ODR_G1][ODR_G0][FS_G1][FS_G0][0][BW_G1][BW_G0] 167 | // ODR_G[2:0] - Output data rate selection 168 | // FS_G[1:0] - Gyroscope full-scale selection 169 | // BW_G[1:0] - Gyroscope bandwidth selection 170 | 171 | // To disable gyro, set sample rate bits to 0. We'll only set sample 172 | // rate if the gyro is enabled. 173 | if (settings.gyro.enabled) 174 | { 175 | tempRegValue = (settings.gyro.sampleRate & 0x07) << 5; 176 | } 177 | switch (settings.gyro.scale) 178 | { 179 | case 500: 180 | tempRegValue |= (0x1 << 3); 181 | break; 182 | case 2000: 183 | tempRegValue |= (0x3 << 3); 184 | break; 185 | // Otherwise we'll set it to 245 dps (0x0 << 4) 186 | } 187 | tempRegValue |= (settings.gyro.bandwidth & 0x3); 188 | xgWriteByte(CTRL_REG1_G, tempRegValue); 189 | 190 | // CTRL_REG2_G (Default value: 0x00) 191 | // [0][0][0][0][INT_SEL1][INT_SEL0][OUT_SEL1][OUT_SEL0] 192 | // INT_SEL[1:0] - INT selection configuration 193 | // OUT_SEL[1:0] - Out selection configuration 194 | xgWriteByte(CTRL_REG2_G, 0x00); 195 | 196 | // CTRL_REG3_G (Default value: 0x00) 197 | // [LP_mode][HP_EN][0][0][HPCF3_G][HPCF2_G][HPCF1_G][HPCF0_G] 198 | // LP_mode - Low-power mode enable (0: disabled, 1: enabled) 199 | // HP_EN - HPF enable (0:disabled, 1: enabled) 200 | // HPCF_G[3:0] - HPF cutoff frequency 201 | tempRegValue = settings.gyro.lowPowerEnable ? (1<<7) : 0; 202 | if (settings.gyro.HPFEnable) 203 | { 204 | tempRegValue |= (1<<6) | (settings.gyro.HPFCutoff & 0x0F); 205 | } 206 | xgWriteByte(CTRL_REG3_G, tempRegValue); 207 | 208 | // CTRL_REG4 (Default value: 0x38) 209 | // [0][0][Zen_G][Yen_G][Xen_G][0][LIR_XL1][4D_XL1] 210 | // Zen_G - Z-axis output enable (0:disable, 1:enable) 211 | // Yen_G - Y-axis output enable (0:disable, 1:enable) 212 | // Xen_G - X-axis output enable (0:disable, 1:enable) 213 | // LIR_XL1 - Latched interrupt (0:not latched, 1:latched) 214 | // 4D_XL1 - 4D option on interrupt (0:6D used, 1:4D used) 215 | tempRegValue = 0; 216 | if (settings.gyro.enableZ) tempRegValue |= (1<<5); 217 | if (settings.gyro.enableY) tempRegValue |= (1<<4); 218 | if (settings.gyro.enableX) tempRegValue |= (1<<3); 219 | if (settings.gyro.latchInterrupt) tempRegValue |= (1<<1); 220 | xgWriteByte(CTRL_REG4, tempRegValue); 221 | 222 | // ORIENT_CFG_G (Default value: 0x00) 223 | // [0][0][SignX_G][SignY_G][SignZ_G][Orient_2][Orient_1][Orient_0] 224 | // SignX_G - Pitch axis (X) angular rate sign (0: positive, 1: negative) 225 | // Orient [2:0] - Directional user orientation selection 226 | tempRegValue = 0; 227 | if (settings.gyro.flipX) tempRegValue |= (1<<5); 228 | if (settings.gyro.flipY) tempRegValue |= (1<<4); 229 | if (settings.gyro.flipZ) tempRegValue |= (1<<3); 230 | xgWriteByte(ORIENT_CFG_G, tempRegValue); 231 | } 232 | 233 | void LSM9DS1::initAccel() 234 | { 235 | uint8_t tempRegValue = 0; 236 | 237 | // CTRL_REG5_XL (0x1F) (Default value: 0x38) 238 | // [DEC_1][DEC_0][Zen_XL][Yen_XL][Zen_XL][0][0][0] 239 | // DEC[0:1] - Decimation of accel data on OUT REG and FIFO. 240 | // 00: None, 01: 2 samples, 10: 4 samples 11: 8 samples 241 | // Zen_XL - Z-axis output enabled 242 | // Yen_XL - Y-axis output enabled 243 | // Xen_XL - X-axis output enabled 244 | if (settings.accel.enableZ) tempRegValue |= (1<<5); 245 | if (settings.accel.enableY) tempRegValue |= (1<<4); 246 | if (settings.accel.enableX) tempRegValue |= (1<<3); 247 | 248 | xgWriteByte(CTRL_REG5_XL, tempRegValue); 249 | 250 | // CTRL_REG6_XL (0x20) (Default value: 0x00) 251 | // [ODR_XL2][ODR_XL1][ODR_XL0][FS1_XL][FS0_XL][BW_SCAL_ODR][BW_XL1][BW_XL0] 252 | // ODR_XL[2:0] - Output data rate & power mode selection 253 | // FS_XL[1:0] - Full-scale selection 254 | // BW_SCAL_ODR - Bandwidth selection 255 | // BW_XL[1:0] - Anti-aliasing filter bandwidth selection 256 | tempRegValue = 0; 257 | // To disable the accel, set the sampleRate bits to 0. 258 | if (settings.accel.enabled) 259 | { 260 | tempRegValue |= (settings.accel.sampleRate & 0x07) << 5; 261 | } 262 | switch (settings.accel.scale) 263 | { 264 | case 4: 265 | tempRegValue |= (0x2 << 3); 266 | break; 267 | case 8: 268 | tempRegValue |= (0x3 << 3); 269 | break; 270 | case 16: 271 | tempRegValue |= (0x1 << 3); 272 | break; 273 | // Otherwise it'll be set to 2g (0x0 << 3) 274 | } 275 | if (settings.accel.bandwidth >= 0) 276 | { 277 | tempRegValue |= (1<<2); // Set BW_SCAL_ODR 278 | tempRegValue |= (settings.accel.bandwidth & 0x03); 279 | } 280 | xgWriteByte(CTRL_REG6_XL, tempRegValue); 281 | 282 | // CTRL_REG7_XL (0x21) (Default value: 0x00) 283 | // [HR][DCF1][DCF0][0][0][FDS][0][HPIS1] 284 | // HR - High resolution mode (0: disable, 1: enable) 285 | // DCF[1:0] - Digital filter cutoff frequency 286 | // FDS - Filtered data selection 287 | // HPIS1 - HPF enabled for interrupt function 288 | tempRegValue = 0; 289 | if (settings.accel.highResEnable) 290 | { 291 | tempRegValue |= (1<<7); // Set HR bit 292 | tempRegValue |= (settings.accel.highResBandwidth & 0x3) << 5; 293 | } 294 | xgWriteByte(CTRL_REG7_XL, tempRegValue); 295 | } 296 | 297 | // This is a function that uses the FIFO to accumulate sample of accelerometer and gyro data, average 298 | // them, scales them to gs and deg/s, respectively, and then passes the biases to the main sketch 299 | // for subtraction from all subsequent data. There are no gyro and accelerometer bias registers to store 300 | // the data as there are in the ADXL345, a precursor to the LSM9DS0, or the MPU-9150, so we have to 301 | // subtract the biases ourselves. This results in a more accurate measurement in general and can 302 | // remove errors due to imprecise or varying initial placement. Calibration of sensor data in this manner 303 | // is good practice. 304 | void LSM9DS1::calibrate(bool autoCalc) 305 | { 306 | uint8_t data[6] = {0, 0, 0, 0, 0, 0}; 307 | uint8_t samples = 0; 308 | int ii; 309 | int32_t aBiasRawTemp[3] = {0, 0, 0}; 310 | int32_t gBiasRawTemp[3] = {0, 0, 0}; 311 | 312 | // Turn on FIFO and set threshold to 32 samples 313 | enableFIFO(true); 314 | setFIFO(FIFO_THS, 0x1F); 315 | while (samples < 0x1F) 316 | { 317 | samples = (xgReadByte(FIFO_SRC) & 0x3F); // Read number of stored samples 318 | } 319 | for(ii = 0; ii < samples ; ii++) 320 | { // Read the gyro data stored in the FIFO 321 | readGyro(); 322 | gBiasRawTemp[0] += gx; 323 | gBiasRawTemp[1] += gy; 324 | gBiasRawTemp[2] += gz; 325 | readAccel(); 326 | aBiasRawTemp[0] += ax; 327 | aBiasRawTemp[1] += ay; 328 | aBiasRawTemp[2] += az - (int16_t)(1./aRes); // Assumes sensor facing up! 329 | } 330 | for (ii = 0; ii < 3; ii++) 331 | { 332 | gBiasRaw[ii] = gBiasRawTemp[ii] / samples; 333 | gBias[ii] = calcGyro(gBiasRaw[ii]); 334 | aBiasRaw[ii] = aBiasRawTemp[ii] / samples; 335 | aBias[ii] = calcAccel(aBiasRaw[ii]); 336 | } 337 | 338 | enableFIFO(false); 339 | setFIFO(FIFO_OFF, 0x00); 340 | 341 | if (autoCalc) _autoCalc = true; 342 | } 343 | 344 | void LSM9DS1::calibrateMag(bool loadIn) 345 | { 346 | int i, j; 347 | int16_t magMin[3] = {0, 0, 0}; 348 | int16_t magMax[3] = {0, 0, 0}; // The road warrior 349 | 350 | for (i=0; i<128; i++) 351 | { 352 | while (!magAvailable()) 353 | ; 354 | readMag(); 355 | int16_t magTemp[3] = {0, 0, 0}; 356 | magTemp[0] = mx; 357 | magTemp[1] = my; 358 | magTemp[2] = mz; 359 | for (j = 0; j < 3; j++) 360 | { 361 | if (magTemp[j] > magMax[j]) magMax[j] = magTemp[j]; 362 | if (magTemp[j] < magMin[j]) magMin[j] = magTemp[j]; 363 | } 364 | } 365 | for (j = 0; j < 3; j++) 366 | { 367 | mBiasRaw[j] = (magMax[j] + magMin[j]) / 2; 368 | mBias[j] = calcMag(mBiasRaw[j]); 369 | if (loadIn) 370 | magOffset(j, mBiasRaw[j]); 371 | } 372 | 373 | } 374 | void LSM9DS1::magOffset(uint8_t axis, int16_t offset) 375 | { 376 | if (axis > 2) 377 | return; 378 | uint8_t msb, lsb; 379 | msb = (offset & 0xFF00) >> 8; 380 | lsb = offset & 0x00FF; 381 | mWriteByte(OFFSET_X_REG_L_M + (2 * axis), lsb); 382 | mWriteByte(OFFSET_X_REG_H_M + (2 * axis), msb); 383 | } 384 | 385 | void LSM9DS1::initMag() 386 | { 387 | uint8_t tempRegValue = 0; 388 | 389 | // CTRL_REG1_M (Default value: 0x10) 390 | // [TEMP_COMP][OM1][OM0][DO2][DO1][DO0][0][ST] 391 | // TEMP_COMP - Temperature compensation 392 | // OM[1:0] - X & Y axes op mode selection 393 | // 00:low-power, 01:medium performance 394 | // 10: high performance, 11:ultra-high performance 395 | // DO[2:0] - Output data rate selection 396 | // ST - Self-test enable 397 | if (settings.mag.tempCompensationEnable) tempRegValue |= (1<<7); 398 | tempRegValue |= (settings.mag.XYPerformance & 0x3) << 5; 399 | tempRegValue |= (settings.mag.sampleRate & 0x7) << 2; 400 | mWriteByte(CTRL_REG1_M, tempRegValue); 401 | 402 | // CTRL_REG2_M (Default value 0x00) 403 | // [0][FS1][FS0][0][REBOOT][SOFT_RST][0][0] 404 | // FS[1:0] - Full-scale configuration 405 | // REBOOT - Reboot memory content (0:normal, 1:reboot) 406 | // SOFT_RST - Reset config and user registers (0:default, 1:reset) 407 | tempRegValue = 0; 408 | switch (settings.mag.scale) 409 | { 410 | case 8: 411 | tempRegValue |= (0x1 << 5); 412 | break; 413 | case 12: 414 | tempRegValue |= (0x2 << 5); 415 | break; 416 | case 16: 417 | tempRegValue |= (0x3 << 5); 418 | break; 419 | // Otherwise we'll default to 4 gauss (00) 420 | } 421 | mWriteByte(CTRL_REG2_M, tempRegValue); // +/-4Gauss 422 | 423 | // CTRL_REG3_M (Default value: 0x03) 424 | // [I2C_DISABLE][0][LP][0][0][SIM][MD1][MD0] 425 | // I2C_DISABLE - Disable I2C interace (0:enable, 1:disable) 426 | // LP - Low-power mode cofiguration (1:enable) 427 | // SIM - SPI mode selection (0:write-only, 1:read/write enable) 428 | // MD[1:0] - Operating mode 429 | // 00:continuous conversion, 01:single-conversion, 430 | // 10,11: Power-down 431 | tempRegValue = 0; 432 | if (settings.mag.lowPowerEnable) tempRegValue |= (1<<5); 433 | tempRegValue |= (settings.mag.operatingMode & 0x3); 434 | mWriteByte(CTRL_REG3_M, tempRegValue); // Continuous conversion mode 435 | 436 | // CTRL_REG4_M (Default value: 0x00) 437 | // [0][0][0][0][OMZ1][OMZ0][BLE][0] 438 | // OMZ[1:0] - Z-axis operative mode selection 439 | // 00:low-power mode, 01:medium performance 440 | // 10:high performance, 10:ultra-high performance 441 | // BLE - Big/little endian data 442 | tempRegValue = 0; 443 | tempRegValue = (settings.mag.ZPerformance & 0x3) << 2; 444 | mWriteByte(CTRL_REG4_M, tempRegValue); 445 | 446 | // CTRL_REG5_M (Default value: 0x00) 447 | // [0][BDU][0][0][0][0][0][0] 448 | // BDU - Block data update for magnetic data 449 | // 0:continuous, 1:not updated until MSB/LSB are read 450 | tempRegValue = 0; 451 | mWriteByte(CTRL_REG5_M, tempRegValue); 452 | } 453 | 454 | uint8_t LSM9DS1::accelAvailable() 455 | { 456 | uint8_t status = xgReadByte(STATUS_REG_1); 457 | 458 | return (status & (1<<0)); 459 | } 460 | 461 | uint8_t LSM9DS1::gyroAvailable() 462 | { 463 | uint8_t status = xgReadByte(STATUS_REG_1); 464 | 465 | return ((status & (1<<1)) >> 1); 466 | } 467 | 468 | uint8_t LSM9DS1::tempAvailable() 469 | { 470 | uint8_t status = xgReadByte(STATUS_REG_1); 471 | 472 | return ((status & (1<<2)) >> 2); 473 | } 474 | 475 | uint8_t LSM9DS1::magAvailable(lsm9ds1_axis axis) 476 | { 477 | uint8_t status; 478 | status = mReadByte(STATUS_REG_M); 479 | 480 | return ((status & (1<> axis); 481 | } 482 | 483 | void LSM9DS1::readAccel() 484 | { 485 | uint8_t temp[6]; // We'll read six bytes from the accelerometer into temp 486 | xgReadBytes(OUT_X_L_XL, temp, 6); // Read 6 bytes, beginning at OUT_X_L_XL 487 | ax = (temp[1] << 8) | temp[0]; // Store x-axis values into ax 488 | ay = (temp[3] << 8) | temp[2]; // Store y-axis values into ay 489 | az = (temp[5] << 8) | temp[4]; // Store z-axis values into az 490 | if (_autoCalc) 491 | { 492 | ax -= aBiasRaw[X_AXIS]; 493 | ay -= aBiasRaw[Y_AXIS]; 494 | az -= aBiasRaw[Z_AXIS]; 495 | } 496 | } 497 | 498 | int16_t LSM9DS1::readAccel(lsm9ds1_axis axis) 499 | { 500 | uint8_t temp[2]; 501 | int16_t value; 502 | xgReadBytes(OUT_X_L_XL + (2 * axis), temp, 2); 503 | value = (temp[1] << 8) | temp[0]; 504 | 505 | if (_autoCalc) 506 | value -= aBiasRaw[axis]; 507 | 508 | return value; 509 | } 510 | 511 | void LSM9DS1::readMag() 512 | { 513 | uint8_t temp[6]; // We'll read six bytes from the mag into temp 514 | mReadBytes(OUT_X_L_M, temp, 6); // Read 6 bytes, beginning at OUT_X_L_M 515 | mx = (temp[1] << 8) | temp[0]; // Store x-axis values into mx 516 | my = (temp[3] << 8) | temp[2]; // Store y-axis values into my 517 | mz = (temp[5] << 8) | temp[4]; // Store z-axis values into mz 518 | } 519 | 520 | int16_t LSM9DS1::readMag(lsm9ds1_axis axis) 521 | { 522 | uint8_t temp[2]; 523 | mReadBytes(OUT_X_L_M + (2 * axis), temp, 2); 524 | return (temp[1] << 8) | temp[0]; 525 | } 526 | 527 | void LSM9DS1::readTemp() 528 | { 529 | uint8_t temp[2]; // We'll read two bytes from the temperature sensor into temp 530 | xgReadBytes(OUT_TEMP_L, temp, 2); // Read 2 bytes, beginning at OUT_TEMP_L 531 | temperature = ((int16_t)temp[1] << 8) | temp[0]; 532 | } 533 | 534 | void LSM9DS1::readGyro() 535 | { 536 | uint8_t temp[6]; // We'll read six bytes from the gyro into temp 537 | xgReadBytes(OUT_X_L_G, temp, 6); // Read 6 bytes, beginning at OUT_X_L_G 538 | gx = (temp[1] << 8) | temp[0]; // Store x-axis values into gx 539 | gy = (temp[3] << 8) | temp[2]; // Store y-axis values into gy 540 | gz = (temp[5] << 8) | temp[4]; // Store z-axis values into gz 541 | if (_autoCalc) 542 | { 543 | gx -= gBiasRaw[X_AXIS]; 544 | gy -= gBiasRaw[Y_AXIS]; 545 | gz -= gBiasRaw[Z_AXIS]; 546 | } 547 | } 548 | 549 | int16_t LSM9DS1::readGyro(lsm9ds1_axis axis) 550 | { 551 | uint8_t temp[2]; 552 | int16_t value; 553 | 554 | xgReadBytes(OUT_X_L_G + (2 * axis), temp, 2); 555 | 556 | value = (temp[1] << 8) | temp[0]; 557 | 558 | if (_autoCalc) 559 | value -= gBiasRaw[axis]; 560 | 561 | return value; 562 | } 563 | 564 | float LSM9DS1::calcGyro(int16_t gyro) 565 | { 566 | // Return the gyro raw reading times our pre-calculated DPS / (ADC tick): 567 | return gRes * gyro; 568 | } 569 | 570 | float LSM9DS1::calcAccel(int16_t accel) 571 | { 572 | // Return the accel raw reading times our pre-calculated g's / (ADC tick): 573 | return aRes * accel; 574 | } 575 | 576 | float LSM9DS1::calcMag(int16_t mag) 577 | { 578 | // Return the mag raw reading times our pre-calculated Gs / (ADC tick): 579 | return mRes * mag; 580 | } 581 | 582 | void LSM9DS1::setGyroScale(uint16_t gScl) 583 | { 584 | // Read current value of CTRL_REG1_G: 585 | uint8_t ctrl1RegValue = xgReadByte(CTRL_REG1_G); 586 | // Mask out scale bits (3 & 4): 587 | ctrl1RegValue &= 0xE7; 588 | switch (gScl) 589 | { 590 | case 500: 591 | ctrl1RegValue |= (0x1 << 3); 592 | settings.gyro.scale = 500; 593 | break; 594 | case 2000: 595 | ctrl1RegValue |= (0x3 << 3); 596 | settings.gyro.scale = 2000; 597 | break; 598 | default: // Otherwise we'll set it to 245 dps (0x0 << 4) 599 | settings.gyro.scale = 245; 600 | break; 601 | } 602 | xgWriteByte(CTRL_REG1_G, ctrl1RegValue); 603 | 604 | calcgRes(); 605 | } 606 | 607 | void LSM9DS1::setAccelScale(uint8_t aScl) 608 | { 609 | // We need to preserve the other bytes in CTRL_REG6_XL. So, first read it: 610 | uint8_t tempRegValue = xgReadByte(CTRL_REG6_XL); 611 | // Mask out accel scale bits: 612 | tempRegValue &= 0xE7; 613 | 614 | switch (aScl) 615 | { 616 | case 4: 617 | tempRegValue |= (0x2 << 3); 618 | settings.accel.scale = 4; 619 | break; 620 | case 8: 621 | tempRegValue |= (0x3 << 3); 622 | settings.accel.scale = 8; 623 | break; 624 | case 16: 625 | tempRegValue |= (0x1 << 3); 626 | settings.accel.scale = 16; 627 | break; 628 | default: // Otherwise it'll be set to 2g (0x0 << 3) 629 | settings.accel.scale = 2; 630 | break; 631 | } 632 | xgWriteByte(CTRL_REG6_XL, tempRegValue); 633 | 634 | // Then calculate a new aRes, which relies on aScale being set correctly: 635 | calcaRes(); 636 | } 637 | 638 | void LSM9DS1::setMagScale(uint8_t mScl) 639 | { 640 | // We need to preserve the other bytes in CTRL_REG6_XM. So, first read it: 641 | uint8_t temp = mReadByte(CTRL_REG2_M); 642 | // Then mask out the mag scale bits: 643 | temp &= 0xFF^(0x3 << 5); 644 | 645 | switch (mScl) 646 | { 647 | case 8: 648 | temp |= (0x1 << 5); 649 | settings.mag.scale = 8; 650 | break; 651 | case 12: 652 | temp |= (0x2 << 5); 653 | settings.mag.scale = 12; 654 | break; 655 | case 16: 656 | temp |= (0x3 << 5); 657 | settings.mag.scale = 16; 658 | break; 659 | default: // Otherwise we'll default to 4 gauss (00) 660 | settings.mag.scale = 4; 661 | break; 662 | } 663 | 664 | // And write the new register value back into CTRL_REG6_XM: 665 | mWriteByte(CTRL_REG2_M, temp); 666 | 667 | // We've updated the sensor, but we also need to update our class variables 668 | // First update mScale: 669 | //mScale = mScl; 670 | // Then calculate a new mRes, which relies on mScale being set correctly: 671 | calcmRes(); 672 | } 673 | 674 | void LSM9DS1::setGyroODR(uint8_t gRate) 675 | { 676 | // Only do this if gRate is not 0 (which would disable the gyro) 677 | if ((gRate & 0x07) != 0) 678 | { 679 | // We need to preserve the other bytes in CTRL_REG1_G. So, first read it: 680 | uint8_t temp = xgReadByte(CTRL_REG1_G); 681 | // Then mask out the gyro ODR bits: 682 | temp &= 0xFF^(0x7 << 5); 683 | temp |= (gRate & 0x07) << 5; 684 | // Update our settings struct 685 | settings.gyro.sampleRate = gRate & 0x07; 686 | // And write the new register value back into CTRL_REG1_G: 687 | xgWriteByte(CTRL_REG1_G, temp); 688 | } 689 | } 690 | 691 | void LSM9DS1::setAccelODR(uint8_t aRate) 692 | { 693 | // Only do this if aRate is not 0 (which would disable the accel) 694 | if ((aRate & 0x07) != 0) 695 | { 696 | // We need to preserve the other bytes in CTRL_REG1_XM. So, first read it: 697 | uint8_t temp = xgReadByte(CTRL_REG6_XL); 698 | // Then mask out the accel ODR bits: 699 | temp &= 0x1F; 700 | // Then shift in our new ODR bits: 701 | temp |= ((aRate & 0x07) << 5); 702 | settings.accel.sampleRate = aRate & 0x07; 703 | // And write the new register value back into CTRL_REG1_XM: 704 | xgWriteByte(CTRL_REG6_XL, temp); 705 | } 706 | } 707 | 708 | void LSM9DS1::setMagODR(uint8_t mRate) 709 | { 710 | // We need to preserve the other bytes in CTRL_REG5_XM. So, first read it: 711 | uint8_t temp = mReadByte(CTRL_REG1_M); 712 | // Then mask out the mag ODR bits: 713 | temp &= 0xFF^(0x7 << 2); 714 | // Then shift in our new ODR bits: 715 | temp |= ((mRate & 0x07) << 2); 716 | settings.mag.sampleRate = mRate & 0x07; 717 | // And write the new register value back into CTRL_REG5_XM: 718 | mWriteByte(CTRL_REG1_M, temp); 719 | } 720 | 721 | void LSM9DS1::calcgRes() 722 | { 723 | gRes = ((float) settings.gyro.scale) / 32768.0; 724 | } 725 | 726 | void LSM9DS1::calcaRes() 727 | { 728 | aRes = ((float) settings.accel.scale) / 32768.0; 729 | } 730 | 731 | void LSM9DS1::calcmRes() 732 | { 733 | //mRes = ((float) settings.mag.scale) / 32768.0; 734 | switch (settings.mag.scale) 735 | { 736 | case 4: 737 | mRes = magSensitivity[0]; 738 | break; 739 | case 8: 740 | mRes = magSensitivity[1]; 741 | break; 742 | case 12: 743 | mRes = magSensitivity[2]; 744 | break; 745 | case 16: 746 | mRes = magSensitivity[3]; 747 | break; 748 | } 749 | 750 | } 751 | 752 | void LSM9DS1::configInt(interrupt_select interrupt, uint8_t generator, 753 | h_lactive activeLow, pp_od pushPull) 754 | { 755 | // Write to INT1_CTRL or INT2_CTRL. [interupt] should already be one of 756 | // those two values. 757 | // [generator] should be an OR'd list of values from the interrupt_generators enum 758 | xgWriteByte(interrupt, generator); 759 | 760 | // Configure CTRL_REG8 761 | uint8_t temp; 762 | temp = xgReadByte(CTRL_REG8); 763 | 764 | if (activeLow) temp |= (1<<5); 765 | else temp &= ~(1<<5); 766 | 767 | if (pushPull) temp &= ~(1<<4); 768 | else temp |= (1<<4); 769 | 770 | xgWriteByte(CTRL_REG8, temp); 771 | } 772 | 773 | void LSM9DS1::configInactivity(uint8_t duration, uint8_t threshold, bool sleepOn) 774 | { 775 | uint8_t temp = 0; 776 | 777 | temp = threshold & 0x7F; 778 | if (sleepOn) temp |= (1<<7); 779 | xgWriteByte(ACT_THS, temp); 780 | 781 | xgWriteByte(ACT_DUR, duration); 782 | } 783 | 784 | uint8_t LSM9DS1::getInactivity() 785 | { 786 | uint8_t temp = xgReadByte(STATUS_REG_0); 787 | temp &= (0x10); 788 | return temp; 789 | } 790 | 791 | void LSM9DS1::configAccelInt(uint8_t generator, bool andInterrupts) 792 | { 793 | // Use variables from accel_interrupt_generator, OR'd together to create 794 | // the [generator]value. 795 | uint8_t temp = generator; 796 | if (andInterrupts) temp |= 0x80; 797 | xgWriteByte(INT_GEN_CFG_XL, temp); 798 | } 799 | 800 | void LSM9DS1::configAccelThs(uint8_t threshold, lsm9ds1_axis axis, uint8_t duration, bool wait) 801 | { 802 | // Write threshold value to INT_GEN_THS_?_XL. 803 | // axis will be 0, 1, or 2 (x, y, z respectively) 804 | xgWriteByte(INT_GEN_THS_X_XL + axis, threshold); 805 | 806 | // Write duration and wait to INT_GEN_DUR_XL 807 | uint8_t temp; 808 | temp = (duration & 0x7F); 809 | if (wait) temp |= 0x80; 810 | xgWriteByte(INT_GEN_DUR_XL, temp); 811 | } 812 | 813 | uint8_t LSM9DS1::getAccelIntSrc() 814 | { 815 | uint8_t intSrc = xgReadByte(INT_GEN_SRC_XL); 816 | 817 | // Check if the IA_XL (interrupt active) bit is set 818 | if (intSrc & (1<<6)) 819 | { 820 | return (intSrc & 0x3F); 821 | } 822 | 823 | return 0; 824 | } 825 | 826 | void LSM9DS1::configGyroInt(uint8_t generator, bool aoi, bool latch) 827 | { 828 | // Use variables from accel_interrupt_generator, OR'd together to create 829 | // the [generator]value. 830 | uint8_t temp = generator; 831 | if (aoi) temp |= 0x80; 832 | if (latch) temp |= 0x40; 833 | xgWriteByte(INT_GEN_CFG_G, temp); 834 | } 835 | 836 | void LSM9DS1::configGyroThs(int16_t threshold, lsm9ds1_axis axis, uint8_t duration, bool wait) 837 | { 838 | uint8_t buffer[2]; 839 | buffer[0] = (threshold & 0x7F00) >> 8; 840 | buffer[1] = (threshold & 0x00FF); 841 | // Write threshold value to INT_GEN_THS_?H_G and INT_GEN_THS_?L_G. 842 | // axis will be 0, 1, or 2 (x, y, z respectively) 843 | xgWriteByte(INT_GEN_THS_XH_G + (axis * 2), buffer[0]); 844 | xgWriteByte(INT_GEN_THS_XH_G + 1 + (axis * 2), buffer[1]); 845 | 846 | // Write duration and wait to INT_GEN_DUR_XL 847 | uint8_t temp; 848 | temp = (duration & 0x7F); 849 | if (wait) temp |= 0x80; 850 | xgWriteByte(INT_GEN_DUR_G, temp); 851 | } 852 | 853 | uint8_t LSM9DS1::getGyroIntSrc() 854 | { 855 | uint8_t intSrc = xgReadByte(INT_GEN_SRC_G); 856 | 857 | // Check if the IA_G (interrupt active) bit is set 858 | if (intSrc & (1<<6)) 859 | { 860 | return (intSrc & 0x3F); 861 | } 862 | 863 | return 0; 864 | } 865 | 866 | void LSM9DS1::configMagInt(uint8_t generator, h_lactive activeLow, bool latch) 867 | { 868 | // Mask out non-generator bits (0-4) 869 | uint8_t config = (generator & 0xE0); 870 | // IEA bit is 0 for active-low, 1 for active-high. 871 | if (activeLow == INT_ACTIVE_HIGH) config |= (1<<2); 872 | // IEL bit is 0 for latched, 1 for not-latched 873 | if (!latch) config |= (1<<1); 874 | // As long as we have at least 1 generator, enable the interrupt 875 | if (generator != 0) config |= (1<<0); 876 | 877 | mWriteByte(INT_CFG_M, config); 878 | } 879 | 880 | void LSM9DS1::configMagThs(uint16_t threshold) 881 | { 882 | // Write high eight bits of [threshold] to INT_THS_H_M 883 | mWriteByte(INT_THS_H_M, uint8_t((threshold & 0x7F00) >> 8)); 884 | // Write low eight bits of [threshold] to INT_THS_L_M 885 | mWriteByte(INT_THS_L_M, uint8_t(threshold & 0x00FF)); 886 | } 887 | 888 | uint8_t LSM9DS1::getMagIntSrc() 889 | { 890 | uint8_t intSrc = mReadByte(INT_SRC_M); 891 | 892 | // Check if the INT (interrupt active) bit is set 893 | if (intSrc & (1<<0)) 894 | { 895 | return (intSrc & 0xFE); 896 | } 897 | 898 | return 0; 899 | } 900 | 901 | void LSM9DS1::sleepGyro(bool enable) 902 | { 903 | uint8_t temp = xgReadByte(CTRL_REG9); 904 | if (enable) temp |= (1<<6); 905 | else temp &= ~(1<<6); 906 | xgWriteByte(CTRL_REG9, temp); 907 | } 908 | 909 | void LSM9DS1::enableFIFO(bool enable) 910 | { 911 | uint8_t temp = xgReadByte(CTRL_REG9); 912 | if (enable) temp |= (1<<1); 913 | else temp &= ~(1<<1); 914 | xgWriteByte(CTRL_REG9, temp); 915 | } 916 | 917 | void LSM9DS1::setFIFO(fifoMode_type fifoMode, uint8_t fifoThs) 918 | { 919 | // Limit threshold - 0x1F (31) is the maximum. If more than that was asked 920 | // limit it to the maximum. 921 | uint8_t threshold = fifoThs <= 0x1F ? fifoThs : 0x1F; 922 | xgWriteByte(FIFO_CTRL, ((fifoMode & 0x7) << 5) | (threshold & 0x1F)); 923 | } 924 | 925 | uint8_t LSM9DS1::getFIFOSamples() 926 | { 927 | return (xgReadByte(FIFO_SRC) & 0x3F); 928 | } 929 | 930 | void LSM9DS1::constrainScales() 931 | { 932 | if ((settings.gyro.scale != 245) && (settings.gyro.scale != 500) && 933 | (settings.gyro.scale != 2000)) 934 | { 935 | settings.gyro.scale = 245; 936 | } 937 | 938 | if ((settings.accel.scale != 2) && (settings.accel.scale != 4) && 939 | (settings.accel.scale != 8) && (settings.accel.scale != 16)) 940 | { 941 | settings.accel.scale = 2; 942 | } 943 | 944 | if ((settings.mag.scale != 4) && (settings.mag.scale != 8) && 945 | (settings.mag.scale != 12) && (settings.mag.scale != 16)) 946 | { 947 | settings.mag.scale = 4; 948 | } 949 | } 950 | 951 | void LSM9DS1::xgWriteByte(uint8_t subAddress, uint8_t data) 952 | { 953 | // Whether we're using I2C or SPI, write a byte using the 954 | // gyro-specific I2C address or SPI CS pin. 955 | if (settings.device.commInterface == IMU_MODE_I2C) 956 | I2CwriteByte(_xgAddress, subAddress, data); 957 | else if (settings.device.commInterface == IMU_MODE_SPI) 958 | SPIwriteByte(_xgAddress, subAddress, data); 959 | } 960 | 961 | void LSM9DS1::mWriteByte(uint8_t subAddress, uint8_t data) 962 | { 963 | // Whether we're using I2C or SPI, write a byte using the 964 | // accelerometer-specific I2C address or SPI CS pin. 965 | if (settings.device.commInterface == IMU_MODE_I2C) 966 | return I2CwriteByte(_mAddress, subAddress, data); 967 | else if (settings.device.commInterface == IMU_MODE_SPI) 968 | return SPIwriteByte(_mAddress, subAddress, data); 969 | } 970 | 971 | uint8_t LSM9DS1::xgReadByte(uint8_t subAddress) 972 | { 973 | // Whether we're using I2C or SPI, read a byte using the 974 | // gyro-specific I2C address or SPI CS pin. 975 | if (settings.device.commInterface == IMU_MODE_I2C) 976 | return I2CreadByte(_xgAddress, subAddress); 977 | else if (settings.device.commInterface == IMU_MODE_SPI) 978 | return SPIreadByte(_xgAddress, subAddress); 979 | } 980 | 981 | void LSM9DS1::xgReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count) 982 | { 983 | // Whether we're using I2C or SPI, read multiple bytes using the 984 | // gyro-specific I2C address or SPI CS pin. 985 | if (settings.device.commInterface == IMU_MODE_I2C) 986 | I2CreadBytes(_xgAddress, subAddress, dest, count); 987 | else if (settings.device.commInterface == IMU_MODE_SPI) 988 | SPIreadBytes(_xgAddress, subAddress, dest, count); 989 | } 990 | 991 | uint8_t LSM9DS1::mReadByte(uint8_t subAddress) 992 | { 993 | // Whether we're using I2C or SPI, read a byte using the 994 | // accelerometer-specific I2C address or SPI CS pin. 995 | if (settings.device.commInterface == IMU_MODE_I2C) 996 | return I2CreadByte(_mAddress, subAddress); 997 | else if (settings.device.commInterface == IMU_MODE_SPI) 998 | return SPIreadByte(_mAddress, subAddress); 999 | } 1000 | 1001 | void LSM9DS1::mReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count) 1002 | { 1003 | // Whether we're using I2C or SPI, read multiple bytes using the 1004 | // accelerometer-specific I2C address or SPI CS pin. 1005 | if (settings.device.commInterface == IMU_MODE_I2C) 1006 | I2CreadBytes(_mAddress, subAddress, dest, count); 1007 | else if (settings.device.commInterface == IMU_MODE_SPI) 1008 | SPIreadBytes(_mAddress, subAddress, dest, count); 1009 | } 1010 | 1011 | void LSM9DS1::initSPI() 1012 | { 1013 | pinMode(_xgAddress, OUTPUT); 1014 | digitalWrite(_xgAddress, HIGH); 1015 | pinMode(_mAddress, OUTPUT); 1016 | digitalWrite(_mAddress, HIGH); 1017 | 1018 | SPI.begin(); 1019 | // Maximum SPI frequency is 10MHz, could divide by 2 here: 1020 | SPI.setClockDivider(SPI_CLOCK_DIV2); 1021 | // Data is read and written MSb first. 1022 | SPI.setBitOrder(MSBFIRST); 1023 | // Data is captured on rising edge of clock (CPHA = 0) 1024 | // Base value of the clock is HIGH (CPOL = 1) 1025 | SPI.setDataMode(SPI_MODE0); 1026 | } 1027 | 1028 | void LSM9DS1::SPIwriteByte(uint8_t csPin, uint8_t subAddress, uint8_t data) 1029 | { 1030 | digitalWrite(csPin, LOW); // Initiate communication 1031 | 1032 | // If write, bit 0 (MSB) should be 0 1033 | // If single write, bit 1 should be 0 1034 | SPI.transfer(subAddress & 0x3F); // Send Address 1035 | SPI.transfer(data); // Send data 1036 | 1037 | digitalWrite(csPin, HIGH); // Close communication 1038 | } 1039 | 1040 | uint8_t LSM9DS1::SPIreadByte(uint8_t csPin, uint8_t subAddress) 1041 | { 1042 | uint8_t temp; 1043 | // Use the multiple read function to read 1 byte. 1044 | // Value is returned to `temp`. 1045 | SPIreadBytes(csPin, subAddress, &temp, 1); 1046 | return temp; 1047 | } 1048 | 1049 | void LSM9DS1::SPIreadBytes(uint8_t csPin, uint8_t subAddress, 1050 | uint8_t * dest, uint8_t count) 1051 | { 1052 | // To indicate a read, set bit 0 (msb) of first byte to 1 1053 | uint8_t rAddress = 0x80 | (subAddress & 0x3F); 1054 | // Mag SPI port is different. If we're reading multiple bytes, 1055 | // set bit 1 to 1. The remaining six bytes are the address to be read 1056 | if ((csPin == _mAddress) && count > 1) 1057 | rAddress |= 0x40; 1058 | 1059 | digitalWrite(csPin, LOW); // Initiate communication 1060 | SPI.transfer(rAddress); 1061 | for (int i=0; i 0)) 1092 | delay(1); 1093 | 1094 | if (timeout <= 0) 1095 | return 255; //! Bad! 255 will be misinterpreted as a good value. 1096 | 1097 | data = Wire.read(); // Fill Rx buffer with result 1098 | return data; // Return data read from slave register 1099 | } 1100 | 1101 | uint8_t LSM9DS1::I2CreadBytes(uint8_t address, uint8_t subAddress, uint8_t * dest, uint8_t count) 1102 | { 1103 | int timeout = LSM9DS1_COMMUNICATION_TIMEOUT; 1104 | Wire.beginTransmission(address); // Initialize the Tx buffer 1105 | // Next send the register to be read. OR with 0x80 to indicate multi-read. 1106 | Wire.write(subAddress | 0x80); // Put slave register address in Tx buffer 1107 | 1108 | Wire.endTransmission(true); // Send the Tx buffer, but send a restart to keep connection alive 1109 | uint8_t i = 0; 1110 | Wire.requestFrom(address, count); // Read bytes from slave register address 1111 | while ((Wire.available() < count) && (timeout-- > 0)) 1112 | delay(1); 1113 | if (timeout <= 0) 1114 | return -1; 1115 | 1116 | for (int i=0; i 100 ) 109 | { 110 | SendString(PSTR("BMP ST TO")); 111 | return -9; 112 | } 113 | goto retry; 114 | } 115 | SendStop(); 116 | 117 | */ 118 | SendStart(); 119 | r = SendByte( BMP280_ADDY ); 120 | if( r ) { SendStop(); SendString(PSTR("BMP CC Fault")); return -4; } 121 | SendByte( 0x88 ); 122 | SendStop(); 123 | 124 | SendStart(); 125 | r = SendByte( BMP280_ADDY | 1 ); 126 | if( r ) { SendStop(); SendString(PSTR("BMP CC Fault")); return -5; } 127 | for( i = 0; i < 23; i++ ) 128 | vals[i] = GetByte(0); 129 | vals[24] = GetByte(1); 130 | SendStop(); 131 | 132 | for( i = 0; i < 12; i++ ) 133 | { 134 | valsout[i] = vals[i*2] | (vals[i*2+1]<<8); 135 | } 136 | return 0; 137 | } 138 | 139 | //Returns 6 values. MSB first for Pressure (3 bytes) then Temp (3 bytes) 140 | int GetBMPTelem( uint8_t * vals ) 141 | { 142 | uint8_t i; 143 | 144 | SendStart(); 145 | if( SendByte( BMP280_ADDY ) ) { SendStop(); SendString(PSTR("BMP BB Fault")); return -4; } 146 | SendByte( 0xF7 ); 147 | SendStop(); 148 | 149 | SendStart(); 150 | if( SendByte( BMP280_ADDY | 1 ) ) { SendStop(); SendString(PSTR("BMP BB Fault")); return -5; } 151 | for( i = 0; i < 5; i++ ) 152 | vals[i] = GetByte(0); 153 | vals[5] = GetByte(1); 154 | SendStop(); 155 | 156 | return 0; 157 | } 158 | 159 | 160 | -------------------------------------------------------------------------------- /firmware-c/user/custom_commands.c: -------------------------------------------------------------------------------- 1 | //Copyright 2015 <>< Charles Lohr, see LICENSE file. 2 | 3 | #include 4 | 5 | extern uint8_t last_leds[512*3]; 6 | extern int last_led_count; 7 | 8 | 9 | int ICACHE_FLASH_ATTR CustomCommand(char * buffer, int retsize, char *pusrdata, unsigned short len) 10 | { 11 | char * buffend = buffer; 12 | 13 | switch( pusrdata[1] ) 14 | { 15 | case 'C': case 'c': //Custom command test 16 | { 17 | buffend += ets_sprintf( buffend, "CC" ); 18 | return buffend-buffer; 19 | } 20 | } 21 | return -1; 22 | } 23 | -------------------------------------------------------------------------------- /firmware-c/user/i2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define I2CSPEEDBASE 1 6 | #define I2CNEEDGETBYTE 7 | #define DSDA 5 8 | #define DSCL 4 9 | 10 | #include "static_i2c.h" 11 | #include "bmp280.h" 12 | #include "lsm9ds1.h" 13 | 14 | struct calData 15 | { 16 | uint16_t T1; 17 | int16_t T2; 18 | int16_t T3; 19 | uint16_t P1; 20 | int16_t P2; 21 | int16_t P3; 22 | int16_t P4; 23 | int16_t P5; 24 | int16_t P6; 25 | int16_t P7; 26 | int16_t P8; 27 | int16_t P9; 28 | }; 29 | 30 | static struct calData CD; 31 | 32 | int16_t mag[3]; 33 | int16_t linrottemp[7]; 34 | 35 | 36 | void ICACHE_FLASH_ATTR InitI2Cs() 37 | { 38 | int i; 39 | int r; 40 | ConfigI2C(); 41 | /* while(1) 42 | { 43 | printf( "A\n" ); 44 | os_delay_us(600000); 45 | PIN_DIR_OUTPUT = _BV(DSDA) | _BV(DSCL); 46 | printf( "B\n" ); 47 | os_delay_us(600000); 48 | PIN_DIR_INPUT = _BV(DSDA) | _BV(DSCL); 49 | }*/ 50 | r = InitBMP280(); 51 | printf( "BMP280: %d\n", r ); 52 | r = InitLSM9DS1(); 53 | printf( "LSM9DS1: %d\n", r ); 54 | r = GetBMPCalVals( (uint16_t*)&CD.T1 ); 55 | printf( "BMP Cal Data: %d\n", r ); 56 | for( i = 0; i < 12; i++ ) 57 | { 58 | printf( " %d", ((int16_t*)&CD.T1)[i] ); 59 | } 60 | printf( "\n" ); 61 | } 62 | 63 | #define BMP280_S32_t int32_t 64 | #define BMP280_S64_t long long 65 | #define BMP280_U32_t uint32_t 66 | #define BMP280_U64_t unsigned long long 67 | 68 | BMP280_S32_t t_fine; 69 | 70 | BMP280_S32_t bmp280_compensate_T_int32(BMP280_S32_t adc_T) 71 | { 72 | BMP280_S32_t var1, var2, T; 73 | var1 = ((((adc_T>>3) - ((BMP280_S32_t)CD.T1<<1))) * ((BMP280_S32_t)CD.T2)) >> 11; 74 | var2 = (((((adc_T>>4) - ((BMP280_S32_t)CD.T1)) * ((adc_T>>4) - ((BMP280_S32_t)CD.T1))) >> 12) * 75 | ((BMP280_S32_t)CD.T3)) >> 14; 76 | t_fine = var1 + var2; 77 | T = (t_fine * 5 + 128) >> 8; 78 | return T; 79 | } 80 | 81 | BMP280_U32_t bmp280_compensate_P_int64(BMP280_S32_t adc_P) 82 | { 83 | BMP280_S64_t var1, var2, p; 84 | var1 = ((BMP280_S64_t)t_fine) - 128000; 85 | var2 = var1 * var1 * (BMP280_S64_t)CD.P6; 86 | var2 = var2 + ((var1*(BMP280_S64_t)CD.P5)<<17); 87 | var2 = var2 + (((BMP280_S64_t)CD.P4)<<35); 88 | var1 = ((var1 * var1 * (BMP280_S64_t)CD.P3)>>8) + ((var1 * (BMP280_S64_t)CD.P2)<<12); 89 | var1 = (((((BMP280_S64_t)1)<<47)+var1))*((BMP280_S64_t)CD.P1)>>33; 90 | if (var1 == 0) 91 | { 92 | return 0; // avoid exception caused by division by zero 93 | } 94 | p = 1048576-adc_P; 95 | p = (((p<<31)-var2)*3125)/var1; 96 | var1 = (((BMP280_S64_t)CD.P9) * (p>>13) * (p>>13)) >> 25; 97 | var2 = (((BMP280_S64_t)CD.P8) * p) >> 19; 98 | p = ((p + var1 + var2) >> 8) + (((BMP280_S64_t)CD.P7)<<4); 99 | 100 | return (BMP280_U32_t)p; 101 | } 102 | 103 | void ReadI2Cs() 104 | { 105 | uint8_t bmptelem[6]; 106 | 107 | ReadM( mag ); 108 | ReadAG( linrottemp ); 109 | GetBMPTelem( bmptelem ); 110 | 111 | int bmpP = ( (bmptelem[0]<<16)|(bmptelem[1]<<8)|bmptelem[2])>>4; 112 | int bmpT = ( (bmptelem[3]<<16)|(bmptelem[4]<<8)|bmptelem[5])>>4; 113 | 114 | int bmpTo = bmp280_compensate_T_int32( bmpT ); 115 | int bmpPo = bmp280_compensate_P_int64( bmpP ); 116 | 117 | printf( " %5d %5d %5d / %5d %5d %5d / %5d %5d %5d / %10d %10d\n", mag[0], mag[1], mag[2], linrottemp[0], linrottemp[1], linrottemp[2], linrottemp[3], linrottemp[4], linrottemp[5], bmpPo, bmpTo ); 118 | 119 | } 120 | 121 | 122 | 123 | int ICACHE_FLASH_ATTR ReadFastAcc( short * data ) 124 | { 125 | int r, status; 126 | 127 | SendStart(); 128 | r = SendByte( AG_ADDY ); 129 | SendByte( 0x2f ); 130 | SendStop(); 131 | 132 | if( r ) { printf( "SRR: %d\n",r ) return -1; } 133 | 134 | SendStart(); 135 | SendByte( AG_ADDY | 1 ); 136 | status = GetByte( 1 ) & 0x3f; 137 | SendStop(); 138 | 139 | if( status == 0 ) return 0; 140 | printf( "." ); 141 | 142 | 143 | //Got data! 144 | SendStart(); 145 | r = SendByte( AG_ADDY ); 146 | if( r ) { SendStop(); printf( "AGF\n" ); return -1; } 147 | SendByte( 0x18 ); //Gyro 148 | SendStop(); 149 | 150 | SendStart(); 151 | SendByte( AG_ADDY | 1 ); 152 | data[0] = LSM9LR16(0); 153 | data[1] = LSM9LR16(0); 154 | data[2] = LSM9LR16(1); 155 | SendStop(); 156 | 157 | //Got data! 158 | SendStart(); 159 | r = SendByte( AG_ADDY ); 160 | if( r ) { SendStop(); printf( "AGF\n" ); return -1; } 161 | SendByte( 0x28 ); 162 | SendStop(); 163 | 164 | 165 | SendStart(); 166 | SendByte( AG_ADDY | 1 ); 167 | data[3] = LSM9LR16(0); 168 | data[4] = LSM9LR16(0); 169 | data[5] = LSM9LR16(1); 170 | SendStop(); 171 | 172 | 173 | 174 | 175 | return 1; 176 | } 177 | 178 | void ICACHE_FLASH_ATTR SetupForFastAcc( int yes ) 179 | { 180 | int r; 181 | if( yes ) 182 | { 183 | SendStart(); 184 | r = SendByte( AG_ADDY ); 185 | if( r ) { SendStop(); SendString(PSTR("AG Fault")); return; } 186 | SendByte( 0x1E ); 187 | SendByte( 0b00111000 ); //0x1E: Disable Gyro 188 | SendByte( 0b00111000 ); //0x1F: Turn on Accelerometer 189 | SendByte( 0b10111100 ); //0x20: Accelerometer, 476 Hz, full-scale=+/-4G, Bandwidth determined by Acc speed. 190 | SendByte( 0b00000000 ); //0x21: Dubious: Disable high-resolution mode. 191 | SendByte( 0b01000100 ); //0x22: LSB in lower, Autoincrement, push-pull, etc. TODO: Block update? 192 | 193 | // v DRDY mask bit? 194 | SendByte( 0b00011010 ); //0x23: DA Timer Enabled, FIFO Enabled Don't stop on FIFO. 195 | SendStop(); 196 | 197 | 198 | SendStart(); 199 | r = SendByte( AG_ADDY ); 200 | if( r ) { SendStop(); SendString(PSTR("AG FaultC")); return; } 201 | SendByte( 0x10 ); 202 | SendByte( 0b10111011 ); //Gyro ODR=467Hz, Cutoff=100Hz, 2000DPS 203 | SendByte( 0b00000000 ); //XXX Consider making outsel 10, to enable LPF2 204 | SendByte( 0b00000000 ); //Highpass = off. 205 | SendStop(); 206 | 207 | 208 | } 209 | else 210 | { 211 | int r; 212 | SendStart(); 213 | r = SendByte( AG_ADDY ); 214 | if( r ) { SendStop(); SendString(PSTR("AG Fault")); return; } 215 | SendByte( 0x1E ); 216 | SendByte( 0b00111000 ); //0x1E: Enable Gyro 217 | SendByte( 0b00111000 ); //0x1F: Turn on Accelerometer 218 | SendByte( 0b01011000 ); //0x20: Accelerometer, 50Hz, +/- 8g, (less than 50Hz) bandwidth 219 | SendByte( 0b00000000 ); //0x21: Dubious: Disable high-resolution mode. 220 | SendByte( 0b01000100 ); //0x22: LSB in lower, Autoincrement, push-pull, etc. TODO: Block update? 221 | SendByte( 0b00011010 ); //0x23: Temp in FIFO. DA Timer Enabled, FIFO Enabled Don't stop on FIFO. 222 | SendStop(); 223 | 224 | 225 | SendStart(); 226 | r = SendByte( AG_ADDY ); 227 | if( r ) { SendStop(); SendString(PSTR("AG FaultC")); return; } 228 | SendByte( 0x10 ); 229 | SendByte( 0b01011011 ); //Gyro ODR=59.5Hz, Cutoff=19Hz, 2000DPS 230 | SendByte( 0b00000000 ); //XXX Consider making outsel 10, to enable LPF2 231 | SendByte( 0b00000000 ); //Highpass = off. 232 | SendStop(); 233 | 234 | } 235 | } 236 | 237 | -------------------------------------------------------------------------------- /firmware-c/user/i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef _I2C_H 2 | #define _I2C_H 3 | 4 | void InitI2Cs(); 5 | void ReadI2Cs(); 6 | 7 | void SetupForFastAcc( int yes ); 8 | int ReadFastAcc( short * data ); //Returns 0 if no samples read, 1 if samples read, -1 on error. 9 | 10 | #endif 11 | 12 | -------------------------------------------------------------------------------- /firmware-c/user/lsm9ds1.h: -------------------------------------------------------------------------------- 1 | // This is intended to be included in the main .c, after static_i2c.h 2 | 3 | //#define AG_ADDY 0xD4 4 | //#define MA_ADDY 0x38 5 | #define AG_ADDY 0xD6 6 | #define MA_ADDY 0x3C 7 | 8 | 9 | #define PSTR 10 | #define SendString printf 11 | #define _delay_us os_delay_us 12 | 13 | int InitLSM9DS1() 14 | { 15 | int r; 16 | SendStart(); 17 | r = SendByte( 0 ); 18 | if( !r ) { SendStop(); SendString(PSTR("I2C Fault")); return -2; } 19 | SendStop(); 20 | 21 | SendStart(); 22 | r = SendByte( AG_ADDY ); 23 | if( r ) { SendStop(); SendString(PSTR("AG Fault")); return -3; } 24 | SendByte( 0x22 ); 25 | SendByte( 0x81 ); //Reboot 26 | SendStop(); 27 | 28 | SendStart(); 29 | r = SendByte( MA_ADDY ); 30 | if( r ) { SendStop(); SendString(PSTR("MA Fault")); return -14; } 31 | SendByte( 0x21 ); 32 | SendByte( 0xC0 ); //Reboot 33 | SendStop(); 34 | 35 | 36 | _delay_us( 20000 ); 37 | 38 | 39 | SendStart(); 40 | r = SendByte( AG_ADDY ); 41 | if( r ) { SendStop(); SendString(PSTR("AG FaultB")); return -5; } 42 | SendByte( 0x22 ); 43 | SendByte( 0b00000110 ); //Auto increment! (Address in lower, too!) 44 | SendStop(); 45 | 46 | 47 | SendStart(); 48 | r = SendByte( AG_ADDY ); 49 | if( r ) { SendStop(); SendString(PSTR("AG FaultA")); return -6; } 50 | SendByte( 0x2E ); 51 | SendByte( 0b11000011 ); //FIFO mode, 3 deep. 52 | SendStop(); 53 | 54 | SendStart(); 55 | r = SendByte( AG_ADDY ); 56 | if( r ) { SendStop(); SendString(PSTR("AG FaultC")); return -7; } 57 | SendByte( 0x10 ); 58 | SendByte( 0b01011011 ); //Gyro ODR=59.5Hz, Cutoff=19Hz, 2000DPS 59 | SendByte( 0b00000000 ); //XXX Consider making outsel 10, to enable LPF2 60 | SendByte( 0b00000000 ); //Highpass = off. 61 | SendStop(); 62 | 63 | SendStart(); 64 | r = SendByte( AG_ADDY ); 65 | if( r ) { SendStop(); SendString(PSTR("AG Fault")); return -8; } 66 | SendByte( 0x1E ); 67 | SendByte( 0b00111000 ); //0x1E: Enable Gyro 68 | SendByte( 0b00111000 ); //0x1F: Turn on Accelerometer 69 | SendByte( 0b01011000 ); //0x20: Accelerometer, 50Hz, +/- 8g, (less than 50Hz) bandwidth 70 | SendByte( 0b00000000 ); //0x21: Dubious: Disable high-resolution mode. 71 | SendByte( 0b01000100 ); //0x22: LSB in lower, Autoincrement, push-pull, etc. TODO: Block update? 72 | SendByte( 0b00011010 ); //0x23: Temp in FIFO. DA Timer Enabled, FIFO Enabled Don't stop on FIFO. 73 | SendStop(); 74 | 75 | SendStart(); 76 | r = SendByte( MA_ADDY ); 77 | if( r ) { SendStop(); SendString(PSTR("MA Fault")); return -9; } 78 | SendByte( 0x20 ); 79 | SendByte( 0b11111110 ); //Temp Comp on. None of this register makes any sense. 80 | SendByte( 0b00000000 ); //+/- 4 Gauss 81 | SendByte( 0b00000000 ); //I2C, Continuous Conversion 82 | SendByte( 0b00001100 ); //(LSB first) High performance. 83 | SendByte( 0b01000000 ); //BDU Enabled. 84 | SendStop(); 85 | 86 | return 0; 87 | } 88 | 89 | 90 | 91 | uint16_t LSM9LR16( uint8_t nak ) 92 | { 93 | uint16_t ret = GetByte(0); 94 | ret |= GetByte( nak )<<8; 95 | return ret; 96 | } 97 | 98 | 99 | #if 0 100 | 101 | //This function will not be maintained. 102 | int ReadAGM( int16_t * LINROTMAG ) 103 | { 104 | int r; 105 | int status = 0; 106 | int timeout = 0; 107 | 108 | retry: 109 | SendStart(); 110 | r = SendByte( AG_ADDY ); 111 | 112 | //Not sure why, but this seems to happen frequently. 113 | if( r ) { 114 | SendStop(); 115 | timeout++; 116 | if( timeout < 10 ) 117 | goto retry; 118 | else 119 | return -81; 120 | } 121 | 122 | SendByte( 0x17 ); 123 | SendStop(); 124 | 125 | SendStart(); 126 | SendByte( AG_ADDY | 1 ); 127 | status = GetByte( 1 ); 128 | SendStop(); 129 | 130 | //???? What is going on here? 131 | if( status <= 0 ) { timeout++; if( timeout < 10 ) goto retry; else return -15; } 132 | 133 | SendStart(); 134 | r = SendByte( AG_ADDY ); 135 | if( r ) { SendStop(); return -2; } 136 | SendByte( 0x28 ); 137 | SendStop(); 138 | 139 | SendStart(); 140 | SendByte( AG_ADDY | 1 ); 141 | LINROTMAG[0] = LSM9LR16(0); 142 | LINROTMAG[1] = LSM9LR16(0); 143 | LINROTMAG[2] = LSM9LR16(1); 144 | SendStop(); 145 | 146 | SendStart(); 147 | r = SendByte( AG_ADDY ); 148 | if( r ) { SendStop(); return -3; } 149 | SendByte( 0x18 ); 150 | SendStop(); 151 | 152 | SendStart(); 153 | SendByte( AG_ADDY | 1 ); 154 | LINROTMAG[3] = LSM9LR16(0); 155 | LINROTMAG[4] = LSM9LR16(0); 156 | LINROTMAG[5] = LSM9LR16(1); 157 | SendStop(); 158 | 159 | 160 | SendStart(); 161 | r = SendByte( MA_ADDY ); 162 | if( r ) { SendStop(); return -4; } 163 | SendByte( 0x28 ); 164 | SendStop(); 165 | 166 | SendStart(); 167 | SendByte( MA_ADDY | 1 ); 168 | LINROTMAG[6] = LSM9LR16(0); 169 | LINROTMAG[7] = LSM9LR16(0); 170 | LINROTMAG[8] = LSM9LR16(1); 171 | SendStop(); 172 | 173 | 174 | SendStart(); 175 | r = SendByte( AG_ADDY ); 176 | if( r ) { SendStop(); return -5; } 177 | SendByte( 0x15 ); 178 | SendStop(); 179 | 180 | SendStart(); 181 | SendByte( AG_ADDY | 1 ); 182 | LINROTMAG[9] = LSM9LR16(1); //Temp 183 | SendStop(); 184 | 185 | 186 | return status; 187 | } 188 | #endif 189 | 190 | //This seems to be pretty solid now. 191 | int ReadM( int16_t * MAG ) 192 | { 193 | SendStart(); 194 | uint8_t r = SendByte( MA_ADDY ); 195 | if( r ) { SendStop(); return -4; } 196 | SendByte( 0x27 ); 197 | SendStop(); 198 | 199 | SendStart(); 200 | SendByte( MA_ADDY | 1 ); 201 | r = GetByte( 0 ); 202 | 203 | if( !(r&0x08) ) 204 | { 205 | //No new data ready. 206 | GetByte( 1 ); //Need a nac. 207 | SendStop(); 208 | return -2; 209 | } 210 | 211 | MAG[0] = LSM9LR16(0); 212 | MAG[1] = LSM9LR16(0); 213 | MAG[2] = LSM9LR16(1); 214 | SendStop(); 215 | return 0; 216 | } 217 | 218 | //Returns # of entries left in fifo if ok. 219 | int ReadAG( int16_t * LINROT ) 220 | { 221 | int r; 222 | int status = 0; 223 | 224 | /* retry: 225 | SendStart(); 226 | r = SendByte( AG_ADDY ); 227 | 228 | //Not sure why, but this seems to happen frequently. 229 | if( r ) { 230 | SendStop(); 231 | timeout++; 232 | if( timeout < 10 ) 233 | goto retry; 234 | else 235 | return -81; 236 | } 237 | 238 | SendByte( 0x17 ); 239 | SendStop(); 240 | */ 241 | SendStart(); 242 | r = SendByte( AG_ADDY ); 243 | SendByte( 0x2f ); 244 | SendStop(); 245 | 246 | SendStart(); 247 | SendByte( AG_ADDY | 1 ); 248 | status = GetByte( 1 ); 249 | SendStop(); 250 | 251 | if( status == 0 ) return 0; 252 | 253 | SendStart(); 254 | r = SendByte( AG_ADDY ); 255 | if( r ) { SendStop(); return -2; } 256 | SendByte( 0x28 ); 257 | SendStop(); 258 | 259 | SendStart(); 260 | SendByte( AG_ADDY | 1 ); 261 | LINROT[0] = LSM9LR16(0); 262 | LINROT[1] = LSM9LR16(0); 263 | LINROT[2] = LSM9LR16(1); 264 | SendStop(); 265 | 266 | SendStart(); 267 | r = SendByte( AG_ADDY ); 268 | if( r ) { SendStop(); return -3; } 269 | SendByte( 0x18 ); 270 | SendStop(); 271 | 272 | SendStart(); 273 | SendByte( AG_ADDY | 1 ); 274 | LINROT[3] = LSM9LR16(0); 275 | LINROT[4] = LSM9LR16(0); 276 | LINROT[5] = LSM9LR16(1); 277 | SendStop(); 278 | 279 | SendStart(); 280 | r = SendByte( AG_ADDY ); 281 | if( r ) { SendStop(); return -5; } 282 | SendByte( 0x15 ); 283 | SendStop(); 284 | 285 | SendStart(); 286 | SendByte( AG_ADDY | 1 ); 287 | LINROT[6] = LSM9LR16(1); //Temp 288 | SendStop(); 289 | 290 | 291 | return status; 292 | } 293 | -------------------------------------------------------------------------------- /firmware-c/user/promiscuous.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "promiscuous.h" 4 | 5 | struct RxControl { 6 | signed rssi:8; 7 | unsigned rate:4; 8 | unsigned is_group:1; 9 | unsigned:1; 10 | unsigned sig_mode:2; 11 | unsigned legacy_length:12; 12 | unsigned damatch0:1; 13 | unsigned damatch1:1; 14 | unsigned bssidmatch0:1; 15 | unsigned bssidmatch1:1; 16 | unsigned MCS:7; 17 | unsigned CWB:1; 18 | unsigned HT_length:16; 19 | unsigned Smoothing:1; 20 | unsigned Not_Sounding:1; 21 | unsigned:1; 22 | unsigned Aggregation:1; 23 | unsigned STBC:2; 24 | unsigned FEC_CODING:1; 25 | unsigned SGI:1; 26 | unsigned rxend_state:8; 27 | unsigned ampdu_cnt:8; 28 | unsigned channel:4; 29 | unsigned:12; 30 | }; 31 | 32 | struct LenSeq{ 33 | u16 len; // length of packet 34 | u16 seq; // serial number of packet, the high 12bits are serial number, 35 | // low 14 bits are Fragment number (usually be 0) 36 | u8 addr3[6]; // the third address in packet 37 | }; 38 | 39 | 40 | struct sniffer_buf { 41 | struct RxControl rx_ctrl; 42 | u8 buf[36 ]; // head of ieee80211 packet 43 | u16 cnt; // number count of packet 44 | struct LenSeq lenseq[1]; //length of packet 45 | }; 46 | 47 | struct sniffer_buf2{ 48 | struct RxControl rx_ctrl; 49 | u8 buf[112]; 50 | u16 cnt; 51 | u16 len; //length of packet 52 | }; 53 | 54 | 55 | 56 | void wifi_promiscuous_cb(uint8 *buf, uint16 len) 57 | { 58 | // struct RxControl *rx = (struct RxControl*) buf; 59 | // printf( "RX: %d %d %d\n", len, rx->MCS, rx->channel ); 60 | int i; 61 | struct RxControl * rx_ctrl; 62 | int mdata = len; 63 | uint8_t * ldat; 64 | int padct = 0; 65 | 66 | if (len == 12){ 67 | //Packet too rough to get real data. 68 | return; 69 | } else if (len == 128) { 70 | struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf; 71 | rx_ctrl = &sniffer->rx_ctrl; 72 | ldat = sniffer->buf; 73 | mdata = 112; 74 | padct = sniffer->len - 112; 75 | if( padct < 0 ) padct = 0; 76 | } else { 77 | struct sniffer_buf *sniffer = (struct sniffer_buf*) buf; 78 | rx_ctrl = &sniffer->rx_ctrl; 79 | ldat = sniffer->buf; 80 | mdata = 36; 81 | } 82 | 83 | #define RADIOTAPLEN (8+4+3) 84 | 85 | 86 | if( ldat && mdata > 40 ) 87 | { 88 | int i, match; 89 | 90 | 91 | uint32_t dt = 0; 92 | uint32_t now = system_get_time(); 93 | 94 | if( memcmp( ldat, "\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff", 10 ) == 0 ) 95 | { 96 | // printf( "%d ", rx_ctrl->rssi ); 97 | int wordlen = ldat[37]; 98 | if( mdata > wordlen + 37 ) 99 | { 100 | if( wordlen >= SSIDMAX ) wordlen = SSIDMAX-1; 101 | uint32_t dt = 0; 102 | uint32_t now = system_get_time(); 103 | for( i = 0; i < CLIENTENTRIES; i++ ) 104 | { 105 | struct ClientEntry * ce = &cle[i]; 106 | uint32_t diff = now - ce->time; 107 | if( diff > dt ) 108 | { 109 | dt = diff; 110 | match = i; 111 | } 112 | if( ets_memcmp( ce->mac, ldat+10, 6 ) == 0 ) 113 | { 114 | match = i; 115 | dt = 0xffffffff; 116 | } 117 | } 118 | 119 | ets_memcpy( cle[match].mac, ldat+10, 6 ); 120 | cle[match].time = now; 121 | cle[match].last = rx_ctrl->rssi; 122 | ets_memcpy( cle[match].apname, &ldat[38], wordlen ); 123 | 124 | // for( i = 0; i < wordlen; i++ ) 125 | // { 126 | // printf( "%c", ldat[i+37] ); 127 | // } 128 | } 129 | } 130 | } 131 | 132 | } 133 | 134 | -------------------------------------------------------------------------------- /firmware-c/user/promiscuous.h: -------------------------------------------------------------------------------- 1 | #ifndef _PROMISCUOUS_H 2 | #define _PROMISCUOUS_H 3 | 4 | #define CLIENTENTRIES 20 5 | #define SSIDMAX 20 6 | 7 | struct ClientEntry 8 | { 9 | uint8_t mac[6]; 10 | char apname[SSIDMAX-1]; 11 | uint32_t time; 12 | int8_t last; 13 | }; 14 | 15 | struct ClientEntry cle[CLIENTENTRIES]; 16 | 17 | #endif 18 | 19 | 20 | -------------------------------------------------------------------------------- /firmware-c/user/static_i2c.h: -------------------------------------------------------------------------------- 1 | //Copyright 2012-2013 <>< Charles Lohr 2 | //This file may be used for any purposes (commercial or private) just please leave this copyright notice in there somewhere. 3 | 4 | //Generic I2C static library for AVRs (ATMega and ATTinys) 5 | 6 | //Include this in your .c file only!!! 7 | //Include it after the following are defined: 8 | 9 | // DSDA -> Pin number the SDA line is on. 10 | // DSCL -> Pin number the SCL line is on. 11 | // 12 | // DPORTNAME -> Port name, i.e. A, B, C, D 13 | // 14 | // I2CDELAY_FUNC -> If you wish to override the internal delay functions. 15 | // I2CSPEEDBASE -> define speed base multiplier 1 = normal, 10 = slow, .1 = fast; failure to define this will result in the clock being as fast as possible. 16 | // I2CNEEDGETBYTE -> Do we need to be able to read data? 17 | // 18 | // I2CPREFIX -> #define to be the prefix, i.e. BOB will cause BOBConfigI2C to be generated. 19 | // I2CNOSTATIC -> #define if you want the functions to be generated as not-static code. 20 | // 21 | //NOTE: You must initially configure the port to be outputs on both DSDA and DSCL and set them both to be driven high. 22 | 23 | #ifndef I2CPREFIX 24 | #define I2CPREFIX 25 | #endif 26 | 27 | #ifndef I2CNOSTATIC 28 | #define I2CSTATICODE 29 | #else 30 | #define I2CSTATICODE static 31 | #endif 32 | 33 | #ifndef I2CFNCOLLAPSE 34 | #define INTI2CFNCOLLAPSE( PFX, name ) PFX ## name 35 | #define I2CFNCOLLAPSE( PFX, name ) INTI2CFNCOLLAPSE( PFX, name ) 36 | #endif 37 | 38 | #define DDDR I2CFNCOLLAPSE( DDR, DPORTNAME ) 39 | #define DPIN I2CFNCOLLAPSE( PIN, DPORTNAME ) 40 | #define DPORT I2CFNCOLLAPSE( PORT, DPORTNAME ) 41 | 42 | 43 | #ifndef I2CDELAY_FUNC 44 | #ifdef I2CSPEEDBASE 45 | #define I2CDELAY_FUNC(x) os_delay_us(x) 46 | #else 47 | #define I2CDELAY_FUNC(x) 48 | #endif 49 | #else 50 | 51 | #endif 52 | 53 | 54 | //Default state: 55 | //PORT LOW 56 | //DDR Undriven 57 | 58 | I2CSTATICODE void I2CFNCOLLAPSE( I2CPREFIX, ConfigI2C ) () 59 | { 60 | PIN_OUT_CLEAR = _BV(DSDA) | _BV(DSCL); 61 | PIN_DIR_INPUT = _BV(DSDA) | _BV(DSCL); 62 | 63 | //DPORT &= ~( _BV( DSDA ) | _BV(DSCL) ); 64 | //DDDR &= ~( _BV( DSDA ) | _BV(DSCL) ); 65 | } 66 | 67 | I2CSTATICODE void I2CFNCOLLAPSE( I2CPREFIX, SendStart )() 68 | { 69 | //DDDR |= _BV( DSDA ); 70 | PIN_DIR_OUTPUT = _BV(DSDA); 71 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 72 | //DDDR |= _BV( DSCL ); 73 | PIN_DIR_OUTPUT = _BV(DSCL); 74 | } 75 | 76 | I2CSTATICODE void I2CFNCOLLAPSE( I2CPREFIX, SendStop ) () 77 | { 78 | //DDDR &= ~_BV( DSCL ); 79 | PIN_DIR_INPUT = _BV(DSCL); 80 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 81 | //DDDR &= ~_BV( DSDA ); 82 | PIN_DIR_INPUT = _BV(DSDA); 83 | } 84 | 85 | //Return nonzero on failure. 86 | I2CSTATICODE unsigned char I2CFNCOLLAPSE( I2CPREFIX, SendByte )( unsigned char data ) 87 | { 88 | unsigned char i; 89 | //Assume we are in a started state (DSCL = 0 & DSDA = 0) 90 | /* DO NOT USE 91 | DPORT |= _BV(DSDA); 92 | DDDR |= _BV(DSDA); 93 | */ 94 | for( i = 0; i < 8; i++ ) 95 | { 96 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 97 | 98 | if( data & 0x80 ) 99 | /*DDDR &= ~_BV( DSDA );*/PIN_DIR_INPUT = _BV(DSDA); 100 | else 101 | /*DDDR |= _BV( DSDA );*/PIN_DIR_OUTPUT = _BV(DSDA); 102 | 103 | data<<=1; 104 | 105 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 106 | 107 | /*DDDR &= ~_BV( DSCL );*/PIN_DIR_INPUT = _BV(DSCL); 108 | 109 | I2CDELAY_FUNC( 2 * I2CSPEEDBASE ); 110 | 111 | /*DDDR |= _BV( DSCL );*/PIN_DIR_OUTPUT = _BV(DSCL); 112 | } 113 | 114 | //Immediately after sending last bit, open up DDDR for control. 115 | /*DDDR &= ~_BV( DSDA );*/ PIN_DIR_INPUT = _BV(DSDA); 116 | 117 | I2CDELAY_FUNC( 2 * I2CSPEEDBASE ); 118 | /*DDDR &= ~_BV( DSCL );*/PIN_DIR_INPUT = _BV(DSCL); 119 | I2CDELAY_FUNC( 2 * I2CSPEEDBASE ); 120 | i = PIN_IN & _BV( DSDA ); 121 | /*DDDR |= _BV( DSCL );*/PIN_DIR_OUTPUT = _BV(DSCL); 122 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 123 | 124 | // Leave in open collector. 125 | 126 | return (i)?1:0; 127 | } 128 | 129 | #ifdef I2CNEEDGETBYTE 130 | 131 | I2CSTATICODE unsigned char I2CFNCOLLAPSE( I2CPREFIX, GetByte )( uint8_t send_nak ) 132 | { 133 | unsigned char i; 134 | unsigned char ret = 0; 135 | 136 | /*DDDR &= ~_BV( DSDA );*/PIN_DIR_INPUT = _BV(DSDA); 137 | 138 | for( i = 0; i < 8; i++ ) 139 | { 140 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 141 | 142 | /*DDDR &= ~_BV( DSCL );*/PIN_DIR_INPUT = _BV(DSCL); 143 | 144 | I2CDELAY_FUNC( 2 * I2CSPEEDBASE ); 145 | 146 | ret<<=1; 147 | 148 | if( PIN_IN & _BV( DSDA ) ) 149 | ret |= 1; 150 | 151 | //XXX TODO !!! CONSIDER PLACING THIS DDDR AFTER THE DELAY!!! 152 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 153 | /*DDDR |= _BV( DSCL );*/PIN_DIR_OUTPUT = _BV(DSCL); 154 | } 155 | 156 | //Send ack. 157 | if( send_nak ) 158 | { 159 | } 160 | else 161 | { 162 | /*DDDR |= _BV( DSDA );*/PIN_DIR_OUTPUT = _BV(DSDA); 163 | } 164 | 165 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 166 | /*DDDR &= ~_BV( DSCL );*/PIN_DIR_INPUT = _BV(DSCL); 167 | I2CDELAY_FUNC( 3 * I2CSPEEDBASE ); 168 | /*DDDR |= _BV( DSCL );*/PIN_DIR_OUTPUT = _BV(DSCL); 169 | 170 | I2CDELAY_FUNC( 1 * I2CSPEEDBASE ); 171 | /*DDDR &= ~_BV( DSDA );*/PIN_DIR_INPUT = _BV(DSDA); 172 | 173 | return ret; 174 | } 175 | 176 | #endif 177 | -------------------------------------------------------------------------------- /firmware-c/user/user_main.c: -------------------------------------------------------------------------------- 1 | //Copyright 2015 <>< Charles Lohr, see LICENSE file. 2 | 3 | #include "mem.h" 4 | #include "c_types.h" 5 | #include "user_interface.h" 6 | #include "ets_sys.h" 7 | #include "uart.h" 8 | #include "osapi.h" 9 | #include "espconn.h" 10 | #include "esp82xxutil.h" 11 | #include "commonservices.h" 12 | #include 13 | #include "i2c.h" 14 | #include "promiscuous.h" 15 | #include "http.h" 16 | 17 | #define procTaskPrio 0 18 | #define procTaskQueueLen 1 19 | 20 | 21 | static volatile os_timer_t some_timer; 22 | 23 | //int ICACHE_FLASH_ATTR StartMDNS(); 24 | 25 | void user_rf_pre_init(void) 26 | { 27 | //nothing. 28 | } 29 | 30 | 31 | char * strcat( char * dest, char * src ) 32 | { 33 | return strcat(dest, src ); 34 | } 35 | 36 | 37 | 38 | //Tasks that happen all the time. 39 | 40 | os_event_t procTaskQueue[procTaskQueueLen]; 41 | 42 | 43 | 44 | #define LSMBUFFERSIZE 1024 45 | int lsmhead = 0; 46 | int lsmtail = 0; 47 | int16_t lsmdata[6*LSMBUFFERSIZE]; 48 | int first_acc = 0; 49 | int i2cmode = 0; 50 | 51 | static void ICACHE_FLASH_ATTR procTask(os_event_t *events) 52 | { 53 | 54 | while( i2cmode == 1 ) 55 | { 56 | //Overflow? 57 | if( ((lsmhead+1)%LSMBUFFERSIZE) == lsmtail ) 58 | { 59 | i2cmode = 0; 60 | break; 61 | } 62 | 63 | int r = ReadFastAcc( &lsmdata[6*lsmhead] ); 64 | if( r < 0 ) 65 | { 66 | i2cmode = 0; 67 | } 68 | else if( r == 0 ) 69 | { 70 | break; 71 | } 72 | else if( r ==1 ) 73 | { 74 | lsmhead = (lsmhead+1)%LSMBUFFERSIZE; 75 | } 76 | } 77 | 78 | CSTick( 0 ); 79 | system_os_post(procTaskPrio, 0, 0 ); 80 | } 81 | 82 | 83 | void wifi_promiscuous_cb(uint8 *buf, uint16 len); 84 | 85 | 86 | void ICACHE_FLASH_ATTR system_init_done() 87 | { 88 | //XXX Disabled: Monitor nearby stations. 89 | //wifi_set_channel(1); 90 | //wifi_promiscuous_enable(0); 91 | //wifi_set_promiscuous_rx_icb(wifi_promiscuous_cb); 92 | //wifi_promiscuous_enable(1); 93 | } 94 | 95 | 96 | 97 | static ICACHE_FLASH_ATTR void fastacc() 98 | { 99 | //char mydat[128]; 100 | //int len = URLDecode( mydat, 128, curhttp->pathbuffer+8 ); 101 | 102 | char buf[1024]; 103 | int times = 20; 104 | int len = 0; 105 | int16_t data[10]; 106 | 107 | if( first_acc == 1 ) 108 | { 109 | first_acc = 0; 110 | len += ets_sprintf( buf+len, "gx, gy, gz, ax, ay, az\r\n" ); 111 | } 112 | 113 | for( ; times > 0 && lsmhead != lsmtail; times -- ) 114 | { 115 | int16_t * data = &lsmdata[6*lsmtail]; 116 | lsmtail = (lsmtail+1)%LSMBUFFERSIZE; 117 | 118 | len += ets_sprintf( buf+len, "%d, %d, %d, %d, %d, %d\r\n", data[0], data[1], data[2], data[3], data[4], data[5] ); 119 | } 120 | 121 | if( len ) 122 | { 123 | START_PACK; 124 | PushBlob( buf, len ); 125 | END_TCP_WRITE( curhttp->socket ); 126 | } 127 | 128 | if( i2cmode == 0 ) 129 | { 130 | curhttp->state = HTTP_WAIT_CLOSE; 131 | } 132 | 133 | } 134 | 135 | 136 | 137 | static int ICACHE_FLASH_ATTR custom_http_cb_start_cb( struct HTTPConnection * hc ) 138 | { 139 | if( strcmp( (const char*)hc->pathbuffer, "/d/fastacc.csv" ) == 0 ) 140 | { 141 | lsmhead = 0; 142 | lsmtail = 0; 143 | i2cmode = 1; 144 | first_acc = 1; 145 | SetupForFastAcc(1); 146 | hc->rcb = (void(*)())&fastacc; 147 | hc->bytesleft = 0xfffffffe; 148 | printf( "Got fast acc request.\n" ); 149 | return 0; 150 | } 151 | if( strcmp( (const char*)hc->pathbuffer, "/d/stopacc" ) == 0 ) 152 | { 153 | i2cmode = 0; 154 | SetupForFastAcc(0); 155 | hc->rcb = 0; 156 | hc->bytesleft = 0x0; 157 | return 0; 158 | } 159 | return -1; 160 | } 161 | 162 | 163 | //Timer event. 164 | static void ICACHE_FLASH_ATTR myTimer(void *arg) 165 | { 166 | static int channelchange = 0; 167 | 168 | CSTick( 1 ); 169 | 170 | if( i2cmode == 0 ) 171 | ReadI2Cs(); 172 | 173 | int i; 174 | uint32_t now = system_get_time(); 175 | 176 | /* 177 | //XXX Disabled: monitoring for nearby stations. 178 | 179 | for( i = 0; i < CLIENTENTRIES; i++ ) 180 | { 181 | int v = now - cle[i].time; 182 | if( v < 10000000 ) 183 | { 184 | printf( "%d %s ", cle[i].last, cle[i].apname ); 185 | } 186 | } 187 | 188 | channelchange++; 189 | if( ( channelchange & 0x00f ) == 0 ) 190 | { 191 | int channel = (channelchange>>4) + 1; 192 | wifi_set_channel( channel ); 193 | if( (channelchange>>4) == 13 ) channelchange = 0; 194 | } 195 | 196 | */ 197 | 198 | system_os_post(procTaskPrio, 0, 0 ); 199 | 200 | 201 | /* 202 | os_delay_us(6000); 203 | wifi_fpm_set_sleep_type(MODEM_SLEEP_T); 204 | wifi_fpm_open(); 205 | wifi_fpm_do_sleep( 0xffffffff ); 206 | os_delay_us(1);*/ 207 | } 208 | 209 | void ICACHE_FLASH_ATTR charrx( uint8_t c ) 210 | { 211 | //Called from UART. 212 | } 213 | 214 | 215 | void ICACHE_FLASH_ATTR user_init(void) 216 | { 217 | uart_init(BIT_RATE_115200, BIT_RATE_115200); 218 | uart0_sendStr("\001\xff\r\n\r\n\033cesp8266 ws2812 driver\r\n"); 219 | 220 | 221 | { 222 | struct rst_info * rs = system_get_rst_info(); 223 | if( rs ) 224 | { 225 | printf( "RS: %08x\n", rs->reason ); 226 | printf( "EC: %08x\n", rs->exccause ); 227 | printf( "E1: %08x\n", rs->epc1 ); 228 | printf( "E2: %08x\n", rs->epc2 ); 229 | printf( "E3: %08x\n", rs->epc3 ); 230 | printf( "EA: %08x\n", rs->excvaddr ); 231 | printf( "DP: %08x\n", rs->depc ); 232 | } 233 | } 234 | 235 | 236 | //Uncomment this to force a system restore. 237 | // system_restore(); 238 | 239 | custom_http_cb_start = custom_http_cb_start_cb; 240 | 241 | CSSettingsLoad( 0 ); 242 | CSPreInit(); 243 | CSInit(); 244 | 245 | SetServiceName( "esplocalizer" ); 246 | AddMDNSName( "esp82xx" ); 247 | AddMDNSService( "_http._tcp", "An ESP8266 Webserver", 80 ); 248 | AddMDNSService( "_cn8266._udp", "ESP8266 Backend", 7878 ); 249 | 250 | //Add a process 251 | system_os_task(procTask, procTaskPrio, procTaskQueue, procTaskQueueLen); 252 | 253 | //Timer example 254 | os_timer_disarm(&some_timer); 255 | os_timer_setfn(&some_timer, (os_timer_func_t *)myTimer, NULL); 256 | os_timer_arm(&some_timer, 25, 1); 257 | 258 | InitI2Cs(); 259 | 260 | printf( "Boot Ok.\n" ); 261 | 262 | // wifi_station_disconnect(); 263 | // wifi_set_opmode(NULL_MODE); 264 | 265 | os_delay_us(6000); 266 | 267 | //wifi_set_sleep_type(LIGHT_SLEEP_T); 268 | 269 | //NOTE: Cannot enter sleep if always running idle task. 270 | system_os_post(procTaskPrio, 0, 0 ); 271 | 272 | system_init_done_cb(system_init_done); 273 | } 274 | 275 | 276 | //There is no code in this project that will cause reboots if interrupts are disabled. 277 | void EnterCritical() 278 | { 279 | } 280 | 281 | void ExitCritical() 282 | { 283 | } 284 | 285 | 286 | uint32 user_rf_cal_sector_set(void) { 287 | return 1; 288 | } 289 | 290 | -------------------------------------------------------------------------------- /firmware-c/web/Makefile: -------------------------------------------------------------------------------- 1 | ../esp82xx/web/Makefile -------------------------------------------------------------------------------- /firmware-c/web/execute_reflash.c: -------------------------------------------------------------------------------- 1 | //Copyright 2015 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or 2 | // ColorChord License. You Choose. 3 | 4 | #include 5 | #include 6 | #include 7 | #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) 8 | #include 9 | #include 10 | #endif 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "md5.h" 17 | #include 18 | 19 | struct libusb_device_handle *devh = NULL; 20 | 21 | 22 | 23 | #define BLOCK_SIZE 65536 24 | #define SECTOR_SIZE 4096 25 | #define PADDING 1024 26 | #ifndef NET_MAXTRIES 27 | #define NET_MAXTRIES 10 // In seconds, may be fractional 28 | #endif 29 | #ifndef NET_TIMEOUT 30 | #define NET_TIMEOUT 3.0 // In seconds, may be fractional 31 | #endif 32 | 33 | int sendsize_max = PADDING; 34 | int use_usb = 0; 35 | 36 | 37 | int sockfd; 38 | struct sockaddr_in servaddr,cliaddr; 39 | 40 | int SendData( uint8_t * buffer, int len ) 41 | { 42 | if( use_usb ) 43 | { 44 | int tries = 0; 45 | int r1; 46 | retry: 47 | r1 = libusb_control_transfer( devh, 48 | 0x00, //reqtype (0x80 = Device->PC, 0x00 = PC->Device) 49 | 0xA6, //request 50 | 0x0100, //wValue 51 | 0x0000, //wIndex 52 | buffer, 53 | len, //wLength (more like max length) 54 | 100 ); 55 | if( r1 != len ) 56 | { 57 | printf( "X" ); fflush( stdout ); 58 | tries++; 59 | if( tries < 10 ) 60 | goto retry; 61 | } 62 | return r1; 63 | //printf( "((Sent: %d/%d %c%c))", r1, len, buffer[0], buffer[1] ); 64 | } 65 | else 66 | { 67 | return sendto( sockfd, buffer, len, MSG_NOSIGNAL, (struct sockaddr *)&servaddr,sizeof(servaddr)); 68 | } 69 | } 70 | 71 | 72 | int PushMatch( const char * match ) 73 | { 74 | if( use_usb ) 75 | { 76 | char recvline[10000]; 77 | int tries = 0; 78 | for( tries = 0; tries < NET_MAXTRIES; tries++ ) 79 | { 80 | usleep( 500 ); 81 | 82 | int r2 = libusb_control_transfer( devh, 83 | 0x80, //reqtype (0x80 = in, 0x00 = out) 84 | 0xA7, //request 85 | 0x0100, //wValue 86 | 0x0000, //wIndex 87 | recvline, //wLength (more like max length) 88 | 128, 89 | 100 ); 90 | 91 | if( r2 < 0 ) continue; 92 | 93 | recvline[r2] = 0; 94 | 95 | if( strncmp( recvline, match, strlen( match ) ) == 0 ) //XXX? Should this be memcmp? 96 | { 97 | return 0; 98 | } 99 | 100 | usleep( 1000 ); 101 | } 102 | return 1; 103 | } 104 | else 105 | { 106 | struct timeval tva, tvb; 107 | gettimeofday( &tva, 0 ); 108 | double diff = 0.0; 109 | while( diff < NET_TIMEOUT ) 110 | { 111 | struct pollfd ufds; 112 | ufds.fd = sockfd; 113 | ufds.events = POLLIN; 114 | int rv = poll(&ufds, 1, 100); 115 | if( rv > 0 ) 116 | { 117 | char recvline[10000]; 118 | int n=recvfrom(sockfd,recvline,10000,0,NULL,NULL); 119 | // printf( "%s === %s\n", recvline, match ); 120 | if( strncmp( recvline, match, strlen( match ) ) == 0 ) 121 | { 122 | printf( "Ok - " ); fflush( stdout ); 123 | return 0; 124 | } 125 | } 126 | gettimeofday( &tvb, 0 ); 127 | diff = tvb.tv_sec - tva.tv_sec + 1e-6*(tvb.tv_usec - tva.tv_usec); 128 | } 129 | return 1; 130 | } 131 | } 132 | 133 | uint32_t Push( uint32_t offset, const char * file ) 134 | { 135 | char sendline[1000]; 136 | char recvline[1000]; 137 | 138 | if( offset <= 0 ) 139 | { 140 | fprintf( stderr, "Error: Cannot write to address 0 or before.\n" ); 141 | exit(-2); 142 | } 143 | 144 | FILE * f = fopen( file, "rb" ); 145 | if( !f || feof( f ) ) 146 | { 147 | fprintf( stderr, "Error: cannot open file.\n" ); 148 | exit(-3); 149 | } 150 | 151 | 152 | 153 | int devo = 0; 154 | int lastblock = -1; 155 | int keep_padding = 0; 156 | int retdevo = 0; 157 | while( keep_padding || !feof( f ) ) 158 | { 159 | int tries; 160 | int thissuccess; 161 | char buffer[PADDING]; 162 | char bufferout[2000]; 163 | 164 | int reads = fread( buffer, 1, sendsize_max, f ); 165 | int sendplace = offset + devo; 166 | int sendsize = sendsize_max; 167 | int block = sendplace / BLOCK_SIZE; 168 | 169 | memset( buffer + reads, 0, sendsize-reads ); 170 | 171 | if( block != lastblock ) 172 | { 173 | char se[64]; 174 | int sel = sprintf( se, "FB%d\r\n", block ); 175 | 176 | thissuccess = 0; 177 | for( tries = 0; tries < NET_MAXTRIES; tries++ ) 178 | { 179 | char match[75]; 180 | //printf( "Erase: %d\n", block ); 181 | printf( "B" ); fflush( stdout ); 182 | SendData( se, sel ); 183 | usleep(130000); //Sleep a while when erasing blocks. 184 | sprintf( match, "FB%d", block ); 185 | if( PushMatch(match) == 0 ) { thissuccess = 1; break; } 186 | printf( "Retry.\n" ); 187 | } 188 | if( !thissuccess ) 189 | { 190 | fprintf( stderr, "Error: Timeout in communications.\n" ); 191 | exit( -6 ); 192 | } 193 | 194 | lastblock = block; 195 | } 196 | 197 | 198 | int r = sprintf( bufferout, "FW%d\t%d\t", sendplace, sendsize ); 199 | //printf( "FW: %d\n", sendplace ); 200 | memcpy( bufferout + r, buffer, sendsize ); 201 | 202 | //printf( "bufferout: %d %d\n", sendplace, sendsize ); 203 | printf( "." ); fflush( stdout ); 204 | 205 | thissuccess = 0; 206 | for( tries = 0; tries < 10; tries++ ) 207 | { 208 | char match[75]; 209 | SendData( bufferout, sendsize + r ); 210 | sprintf( match, "FW%d", sendplace ); 211 | 212 | if( PushMatch(match) == 0 ) { thissuccess = 1; break; } 213 | printf( "Retry.\n" ); 214 | } 215 | if( !thissuccess ) 216 | { 217 | fprintf( stderr, "Error: Timeout in communications.\n" ); 218 | exit( -6 ); 219 | } 220 | 221 | if( reads != 0 ) 222 | { 223 | devo += sendsize; 224 | retdevo = devo; 225 | } 226 | 227 | //Tricky: If we are using a smaller sendsize than the pad size, keep padding with 0's. 228 | if( PADDING!=sendsize && (devo & (PADDING-1)) && feof( f ) ) 229 | { 230 | keep_padding = 1; 231 | if( reads == 0 ) 232 | devo += sendsize; 233 | } 234 | else 235 | { 236 | keep_padding = 0; 237 | } 238 | } 239 | 240 | return retdevo; 241 | } 242 | 243 | void ComputeMD5WithKey( char * md5retText, const char * filename, const char * key ) 244 | { 245 | uint8_t retmd5[16]; 246 | MD5_CTX ctx; 247 | FILE * f = fopen( filename, "rb" ); 248 | if( !f ) 249 | { 250 | fprintf( stderr, "Error opening %s\n", filename ); 251 | exit( -9 ); 252 | } 253 | 254 | fseek( f, 0, SEEK_END ); 255 | int l = ftell( f ); 256 | printf("MD5 Size: %d\n", l ); 257 | int padl = ((l-1) / sendsize_max)*sendsize_max+sendsize_max; 258 | printf("MD5 Pad: %d\n", padl ); 259 | fseek( f, 0, SEEK_SET ); 260 | uint8_t data[padl]; 261 | fread( data, l, 1, f ); 262 | fclose( f ); 263 | 264 | memset( data+l, 0, padl-l ); 265 | MD5_Init( &ctx ); 266 | if( !strlen(key) ) 267 | MD5_Update( &ctx, key, strlen( key ) ); 268 | MD5_Update( &ctx, data, padl ); 269 | MD5_Final( retmd5, &ctx ); 270 | 271 | for( l = 0; l < 16; l++ ) 272 | { 273 | sprintf( md5retText + l*2, "%02x", retmd5[l] ); 274 | } 275 | 276 | return; 277 | } 278 | 279 | uint32_t roundup( uint32_t r ) 280 | { 281 | return ((r-1)&(~0xFFF))+0x1000; 282 | } 283 | 284 | 285 | int main(int argc, char**argv) 286 | { 287 | int n; 288 | 289 | char sendline[1000]; 290 | char recvline[1000]; 291 | 292 | char md5_f1[48]; 293 | char md5_f2[48]; 294 | 295 | if (argc < 4 ) 296 | { 297 | printf("usage: pushtodev [IP address] [file_lower] [file_upper] [key (optional)]\n"); 298 | exit(-1); 299 | } 300 | 301 | const char * file1 = argv[2]; 302 | const char * file2 = argv[3]; 303 | 304 | 305 | 306 | if( strcmp( argv[1], "USB" ) == 0 ) 307 | { 308 | int r; 309 | use_usb = 1; 310 | printf( "Connecting by USB\n" ); 311 | fprintf( stderr, "WARNING: USB BURNING IS EXPERIMENTAL AND LIKELY TO CHANGE\n" ); 312 | if( libusb_init(NULL) < 0 ) 313 | { 314 | fprintf( stderr, "Error: Could not initialize libUSB\n" ); 315 | return -1; 316 | } 317 | 318 | 319 | devh = libusb_open_device_with_vid_pid( NULL, 0xabcd, 0x8266 ); 320 | if( !devh ) 321 | { 322 | fprintf( stderr, "Error: Cannot find USB device.\n" ); 323 | return -1; 324 | } 325 | 326 | libusb_detach_kernel_driver( devh, 0 ); //Mouse? 327 | libusb_detach_kernel_driver( devh, 1 ); //Keyboard? 328 | 329 | printf( "Connected.\n" ); 330 | //USB is attached 331 | sendsize_max = 128; 332 | } 333 | else 334 | { 335 | sockfd=socket(AF_INET,SOCK_DGRAM,0); 336 | 337 | bzero(&servaddr,sizeof(servaddr)); 338 | servaddr.sin_family = AF_INET; 339 | servaddr.sin_addr.s_addr=inet_addr(argv[1]); 340 | servaddr.sin_port=htons(BACKEND_PORT); 341 | } 342 | 343 | uint32_t fs1 = Push( 0x080000, file1 ); 344 | uint32_t fs2 = Push( 0x0c0000, file2 ); 345 | 346 | if( !fs1 || !fs2 ) 347 | { 348 | fprintf( stderr, "Error: File size not acceptable.\n" ); 349 | return 0; 350 | } 351 | 352 | const char * dat = ""; 353 | if( argc == 5 ) 354 | { 355 | dat = argv[4]; 356 | } 357 | 358 | ComputeMD5WithKey( md5_f1, file1, dat ); 359 | ComputeMD5WithKey( md5_f2, file2, dat ); 360 | 361 | printf( "%s %s\n", md5_f1, md5_f2 ); 362 | 363 | //FM[from_address]\t[to_address]\t[size]\t[MD5(key+data)]\t[from_address]\t[to_address]\t[size]\t[MD5(key+data)] 364 | 365 | char cmd[1024]; 366 | 367 | sprintf( cmd, "FM%d\t%d\t%d\t%s\t%d\t%d\t%d\t%s\n", 368 | 0x080000, 369 | 0x000000, 370 | fs1, //roundup( fs1 ), 371 | md5_f1, 372 | 0x0C0000, 373 | 0x040000, 374 | fs2, //roundup( fs2 ), 375 | md5_f2 ); 376 | 377 | printf( "Issuing: %s\n", cmd ); 378 | SendData( cmd, strlen(cmd) ); 379 | usleep(10000); 380 | SendData( cmd, strlen(cmd) ); 381 | 382 | struct pollfd ufds; 383 | ufds.fd = sockfd; 384 | ufds.events = POLLIN; 385 | int rv = poll(&ufds, 1, 100); 386 | if( rv > 0 ) 387 | { 388 | char recvline[10000]; 389 | int n=recvfrom(sockfd,recvline,10000,0,NULL,NULL); 390 | 391 | printf( "Response: %s\n",recvline ); 392 | return 0; 393 | } 394 | else 395 | { 396 | printf( "Timeout. Good? Maybe?\n" ); 397 | return 0; 398 | } 399 | 400 | return 0; 401 | } 402 | 403 | 404 | -------------------------------------------------------------------------------- /firmware-c/web/md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: 9 | * Alexander Peslyak, better known as Solar Designer 10 | * 11 | * This software was written by Alexander Peslyak in 2001. No copyright is 12 | * claimed, and the software is hereby placed in the public domain. 13 | * In case this attempt to disclaim copyright and place the software in the 14 | * public domain is deemed null and void, then the software is 15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 16 | * general public under the following terms: 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted. 20 | * 21 | * There's ABSOLUTELY NO WARRANTY, express or implied. 22 | * 23 | * (This is a heavily cut-down "BSD license".) 24 | * 25 | * This differs from Colin Plumb's older public domain implementation in that 26 | * no exactly 32-bit integer data type is required (any 32-bit or wider 27 | * unsigned integer data type will do), there's no compile-time endianness 28 | * configuration, and the function prototypes match OpenSSL's. No code from 29 | * Colin Plumb's implementation has been reused; this comment merely compares 30 | * the properties of the two independent implementations. 31 | * 32 | * The primary goals of this implementation are portability and ease of use. 33 | * It is meant to be fast, but not as fast as possible. Some known 34 | * optimizations are not included to reduce source code size and avoid 35 | * compile-time configuration. 36 | */ 37 | 38 | #ifndef HAVE_OPENSSL 39 | 40 | #include 41 | 42 | #include "md5.h" 43 | 44 | /* 45 | * The basic MD5 functions. 46 | * 47 | * F and G are optimized compared to their RFC 1321 definitions for 48 | * architectures that lack an AND-NOT instruction, just like in Colin Plumb's 49 | * implementation. 50 | */ 51 | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) 52 | #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) 53 | #define H(x, y, z) (((x) ^ (y)) ^ (z)) 54 | #define H2(x, y, z) ((x) ^ ((y) ^ (z))) 55 | #define I(x, y, z) ((y) ^ ((x) | ~(z))) 56 | 57 | /* 58 | * The MD5 transformation for all four rounds. 59 | */ 60 | #define STEP(f, a, b, c, d, x, t, s) \ 61 | (a) += f((b), (c), (d)) + (x) + (t); \ 62 | (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ 63 | (a) += (b); 64 | 65 | /* 66 | * SET reads 4 input bytes in little-endian byte order and stores them 67 | * in a properly aligned word in host byte order. 68 | * 69 | * The check for little-endian architectures that tolerate unaligned 70 | * memory accesses is just an optimization. Nothing will break if it 71 | * doesn't work. 72 | */ 73 | #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) 74 | #define SET(n) \ 75 | (*(MD5_u32plus *)&ptr[(n) * 4]) 76 | #define GET(n) \ 77 | SET(n) 78 | #else 79 | #define SET(n) \ 80 | (ctx->block[(n)] = \ 81 | (MD5_u32plus)ptr[(n) * 4] | \ 82 | ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ 83 | ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ 84 | ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) 85 | #define GET(n) \ 86 | (ctx->block[(n)]) 87 | #endif 88 | 89 | /* 90 | * This processes one or more 64-byte data blocks, but does NOT update 91 | * the bit counters. There are no alignment requirements. 92 | */ 93 | static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) 94 | { 95 | const unsigned char *ptr; 96 | MD5_u32plus a, b, c, d; 97 | MD5_u32plus saved_a, saved_b, saved_c, saved_d; 98 | 99 | ptr = (const unsigned char *)data; 100 | 101 | a = ctx->a; 102 | b = ctx->b; 103 | c = ctx->c; 104 | d = ctx->d; 105 | 106 | do { 107 | saved_a = a; 108 | saved_b = b; 109 | saved_c = c; 110 | saved_d = d; 111 | 112 | /* Round 1 */ 113 | STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) 114 | STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) 115 | STEP(F, c, d, a, b, SET(2), 0x242070db, 17) 116 | STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) 117 | STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) 118 | STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) 119 | STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) 120 | STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) 121 | STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) 122 | STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) 123 | STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) 124 | STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) 125 | STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) 126 | STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) 127 | STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) 128 | STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) 129 | 130 | /* Round 2 */ 131 | STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) 132 | STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) 133 | STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) 134 | STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) 135 | STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) 136 | STEP(G, d, a, b, c, GET(10), 0x02441453, 9) 137 | STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) 138 | STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) 139 | STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) 140 | STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) 141 | STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) 142 | STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) 143 | STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) 144 | STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) 145 | STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) 146 | STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) 147 | 148 | /* Round 3 */ 149 | STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) 150 | STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) 151 | STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) 152 | STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) 153 | STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) 154 | STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) 155 | STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) 156 | STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) 157 | STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) 158 | STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) 159 | STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) 160 | STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) 161 | STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) 162 | STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) 163 | STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) 164 | STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) 165 | 166 | /* Round 4 */ 167 | STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) 168 | STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) 169 | STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) 170 | STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) 171 | STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) 172 | STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) 173 | STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) 174 | STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) 175 | STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) 176 | STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) 177 | STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) 178 | STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) 179 | STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) 180 | STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) 181 | STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) 182 | STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) 183 | 184 | a += saved_a; 185 | b += saved_b; 186 | c += saved_c; 187 | d += saved_d; 188 | 189 | ptr += 64; 190 | } while (size -= 64); 191 | 192 | ctx->a = a; 193 | ctx->b = b; 194 | ctx->c = c; 195 | ctx->d = d; 196 | 197 | return ptr; 198 | } 199 | 200 | void MD5_Init(MD5_CTX *ctx) 201 | { 202 | ctx->a = 0x67452301; 203 | ctx->b = 0xefcdab89; 204 | ctx->c = 0x98badcfe; 205 | ctx->d = 0x10325476; 206 | 207 | ctx->lo = 0; 208 | ctx->hi = 0; 209 | } 210 | 211 | void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) 212 | { 213 | MD5_u32plus saved_lo; 214 | unsigned long used, available; 215 | 216 | saved_lo = ctx->lo; 217 | if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) 218 | ctx->hi++; 219 | ctx->hi += size >> 29; 220 | 221 | used = saved_lo & 0x3f; 222 | 223 | if (used) { 224 | available = 64 - used; 225 | 226 | if (size < available) { 227 | memcpy(&ctx->buffer[used], data, size); 228 | return; 229 | } 230 | 231 | memcpy(&ctx->buffer[used], data, available); 232 | data = (const unsigned char *)data + available; 233 | size -= available; 234 | body(ctx, ctx->buffer, 64); 235 | } 236 | 237 | if (size >= 64) { 238 | data = body(ctx, data, size & ~(unsigned long)0x3f); 239 | size &= 0x3f; 240 | } 241 | 242 | memcpy(ctx->buffer, data, size); 243 | } 244 | 245 | void MD5_Final(unsigned char *result, MD5_CTX *ctx) 246 | { 247 | unsigned long used, available; 248 | 249 | used = ctx->lo & 0x3f; 250 | 251 | ctx->buffer[used++] = 0x80; 252 | 253 | available = 64 - used; 254 | 255 | if (available < 8) { 256 | memset(&ctx->buffer[used], 0, available); 257 | body(ctx, ctx->buffer, 64); 258 | used = 0; 259 | available = 64; 260 | } 261 | 262 | memset(&ctx->buffer[used], 0, available - 8); 263 | 264 | ctx->lo <<= 3; 265 | ctx->buffer[56] = ctx->lo; 266 | ctx->buffer[57] = ctx->lo >> 8; 267 | ctx->buffer[58] = ctx->lo >> 16; 268 | ctx->buffer[59] = ctx->lo >> 24; 269 | ctx->buffer[60] = ctx->hi; 270 | ctx->buffer[61] = ctx->hi >> 8; 271 | ctx->buffer[62] = ctx->hi >> 16; 272 | ctx->buffer[63] = ctx->hi >> 24; 273 | 274 | body(ctx, ctx->buffer, 64); 275 | 276 | result[0] = ctx->a; 277 | result[1] = ctx->a >> 8; 278 | result[2] = ctx->a >> 16; 279 | result[3] = ctx->a >> 24; 280 | result[4] = ctx->b; 281 | result[5] = ctx->b >> 8; 282 | result[6] = ctx->b >> 16; 283 | result[7] = ctx->b >> 24; 284 | result[8] = ctx->c; 285 | result[9] = ctx->c >> 8; 286 | result[10] = ctx->c >> 16; 287 | result[11] = ctx->c >> 24; 288 | result[12] = ctx->d; 289 | result[13] = ctx->d >> 8; 290 | result[14] = ctx->d >> 16; 291 | result[15] = ctx->d >> 24; 292 | 293 | memset(ctx, 0, sizeof(*ctx)); 294 | } 295 | 296 | #endif 297 | -------------------------------------------------------------------------------- /firmware-c/web/md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: 9 | * Alexander Peslyak, better known as Solar Designer 10 | * 11 | * This software was written by Alexander Peslyak in 2001. No copyright is 12 | * claimed, and the software is hereby placed in the public domain. 13 | * In case this attempt to disclaim copyright and place the software in the 14 | * public domain is deemed null and void, then the software is 15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 16 | * general public under the following terms: 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted. 20 | * 21 | * There's ABSOLUTELY NO WARRANTY, express or implied. 22 | * 23 | * See md5.c for more information. 24 | */ 25 | 26 | #ifdef HAVE_OPENSSL 27 | #include 28 | #elif !defined(_MD5_H) 29 | #define _MD5_H 30 | 31 | /* Any 32-bit or wider unsigned integer data type will do */ 32 | typedef unsigned int MD5_u32plus; 33 | 34 | typedef struct { 35 | MD5_u32plus lo, hi; 36 | MD5_u32plus a, b, c, d; 37 | unsigned char buffer[64]; 38 | MD5_u32plus block[16]; 39 | } MD5_CTX; 40 | 41 | extern void MD5_Init(MD5_CTX *ctx); 42 | extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); 43 | extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /firmware-c/web/mfsmaker.c: -------------------------------------------------------------------------------- 1 | 2 | //Copyright 2015 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or 3 | // ColorChord License. You Choose. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define SPI_FLASH_SEC_SIZE 4096 12 | #define MFS_STARTFLASHSECTOR 0x100 13 | #define MFS_START (MFS_STARTFLASHSECTOR*SPI_FLASH_SEC_SIZE) 14 | #define MFS_SECTOR 256 15 | #define MFS_FILENAMELEN 32-8 16 | #define ENTRIES 8192 17 | 18 | #define ENDIAN(x) x//htonl 19 | 20 | struct MFSFileEntry 21 | { 22 | char name[MFS_FILENAMELEN]; 23 | uint32_t start; //From beginning of mfs thing. 24 | uint32_t len; 25 | } mfsfat[ENTRIES]; 26 | 27 | unsigned char mfsdata[131072*8]; 28 | 29 | unsigned long fatpointer = 0; 30 | unsigned long datapointer = 0; 31 | 32 | int main( int argc, char ** argv ) 33 | { 34 | int i; 35 | DIR *d; 36 | struct dirent *dir; 37 | 38 | if( argc != 3 ) 39 | { 40 | fprintf( stderr, "Error: [tool] [directory to pack] [output packed .dat file]\n" ); 41 | return -1; 42 | } 43 | 44 | d = opendir( argv[1] ); 45 | 46 | if (!d) 47 | { 48 | fprintf( stderr, "Error: cannot open folder for packing.\n" ); 49 | return -2; 50 | } 51 | 52 | 53 | memcpy( mfsfat[fatpointer].name, "MPFSMPFS", 8 ); 54 | mfsfat[fatpointer].start = 0; 55 | mfsfat[fatpointer].len = 0; 56 | fatpointer++; 57 | 58 | 59 | while ((dir = readdir(d)) != NULL) 60 | { 61 | if( dir->d_type & DT_REG ) 62 | { 63 | char thisfile[1024]; 64 | struct stat buf; 65 | int dlen = strlen( dir->d_name ); 66 | int sprret = snprintf( thisfile, 1023, "%s/%s", argv[1], dir->d_name ); 67 | 68 | if( sprret > 1023 || sprret < 1 ) 69 | { 70 | fprintf( stderr, "Error processing \"%s\" (snprintf)\n", dir->d_name ); 71 | continue; 72 | } 73 | 74 | int statret = stat( thisfile, &buf ); 75 | 76 | if( statret ) 77 | { 78 | fprintf( stderr, "Error processing \"%s\" (stat)\n", dir->d_name ); 79 | continue; 80 | } 81 | 82 | if( dlen >= MFS_FILENAMELEN ) 83 | { 84 | fprintf( stderr, "Warning: Fle \"%s\" too long.\n", dir->d_name ); 85 | continue; 86 | } 87 | if( fatpointer > ENTRIES ) 88 | { 89 | fprintf( stderr, "Warning: Not enough entries to fit \"%s\".\n", dir->d_name ); 90 | continue; 91 | } 92 | 93 | if( buf.st_size + datapointer > sizeof( mfsdata ) ) 94 | { 95 | fprintf( stderr, "Error: no space left.\n" ); 96 | return -1; 97 | } 98 | 99 | memcpy( mfsfat[fatpointer].name, dir->d_name, dlen ); 100 | mfsfat[fatpointer].start = datapointer; 101 | mfsfat[fatpointer].len = ENDIAN( buf.st_size ); 102 | fatpointer++; 103 | 104 | if( buf.st_size ) 105 | { 106 | FILE * f = fopen( thisfile, "rb" ); 107 | if( !f ) 108 | { 109 | fprintf( stderr, "Error: cannot open \"%s\" for reading.\n", dir->d_name ); 110 | return -9; 111 | } 112 | fread( &mfsdata[datapointer], 1, buf.st_size, f ); 113 | fclose( f ); 114 | int rs = buf.st_size; 115 | rs = (rs+1+MFS_SECTOR)&(~(MFS_SECTOR-1)); 116 | datapointer += rs; 117 | printf( "%s: %d (%ld)\n", thisfile, rs, datapointer ); 118 | } 119 | } 120 | } 121 | 122 | closedir(d); 123 | 124 | int rs = (fatpointer+1)*sizeof(struct MFSFileEntry); 125 | rs = (rs+1+MFS_SECTOR)&(~(MFS_SECTOR-1)); 126 | for( i = 0; i < fatpointer; i++ ) 127 | { 128 | mfsfat[i].start = ENDIAN(mfsfat[i].start + rs ); 129 | } 130 | 131 | printf( "%d %ld\n", rs, datapointer ); 132 | 133 | FILE * f = fopen( argv[2], "w" ); 134 | 135 | if( !f || ferror( f ) ) 136 | { 137 | fprintf( stderr, "Error: cannot open \"%s\" for writing.\n", argv[2] ); 138 | } 139 | fwrite( mfsfat, rs, 1, f ); 140 | fwrite( mfsdata, datapointer, 1, f ); 141 | fclose( f ); 142 | 143 | return 0; 144 | } 145 | 146 | 147 | -------------------------------------------------------------------------------- /firmware-c/web/page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{PAGE_TITLE}} 4 | {{PAGE_SCRIPT}} 5 | 6 | 15 | 16 | 17 |

{{PAGE_HEADING}}


18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 |

Copyright (C) 2015-2016 <>< Charles Lohr, See LICENSE file for more info.

38 |

github-logo 39 | {{PROJECT_NAME}} 40 | {{VERSSTR}}

41 |
42 |
...
43 | 44 | 45 | -------------------------------------------------------------------------------- /firmware-c/web/page/jquery-2.1.4.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnlohr/esplocalizer/f721be42459857843b51c5eaff5be4fd75728382/firmware-c/web/page/jquery-2.1.4.min.js.gz -------------------------------------------------------------------------------- /firmware-c/web/page/menuinterface.js: -------------------------------------------------------------------------------- 1 | //Copyright (C) 2015 <>< Charles Lohr, see LICENSE file for more info. 2 | // 3 | //This particular file may be licensed under the MIT/x11, New BSD or ColorChord Licenses. 4 | var wsUri = "ws://" + location.host + "/d/ws/issue"; 5 | var output; 6 | var websocket; 7 | var commsup = 0; 8 | 9 | var mpfs_start_at = 65536; //1048576; NOTE: If you select 1048576, it will override the 65536 sector, but has much more room. 10 | var flash_scratchpad_at = 524288; 11 | var flash_blocksize = 65536; 12 | var flash_sendsize = 256; 13 | //Push objects that have: 14 | // .request 15 | // .callback = function( ref (this object), data ); 16 | 17 | var workqueue = []; 18 | var wifilines = []; 19 | var workarray = {}; 20 | var lastitem; 21 | 22 | var SystemMessageTimeout = null; 23 | function IssueSystemMessage( msg ) 24 | { 25 | var elem = $( "#SystemMessage" ); 26 | elem.hide(); 27 | elem.html( "" + msg + "" ); 28 | elem.slideToggle( 'fast' ); 29 | if( SystemMessageTimeout != null ) clearTimeout(SystemMessageTimeout); 30 | SystemMessageTimeout = setTimeout( function() { SystemMessageTimeout = null; $( "#SystemMessage" ).fadeOut( 'slow' ) }, 3000 ); 31 | } 32 | 33 | function QueueOperation( command, callback ) 34 | { 35 | if( workarray[command] == 1 ) 36 | { 37 | return; 38 | } 39 | 40 | workarray[command] = 1; 41 | var vp = new Object(); 42 | vp.callback = callback; 43 | vp.request = command; 44 | workqueue.push( vp ); 45 | } 46 | 47 | 48 | function init() 49 | { 50 | GPIOlines = ''; 51 | for(var i=0; i<16; ++i) 52 | GPIOlines += ""+ i 53 | + "" 54 | + "" 55 | + ""; 56 | 57 | $('#MainMenu > tbody:first-child').before( "\ 58 | \ 59 | \ 60 |
\ 61 |
\ 62 |
\n
" 63 | ); 64 | 65 | $('#MainMenu > tbody:last-child').after( "\ 66 | \ 67 | \ 68 |
\ 69 |
\ 70 | Current Configuration: (May deviate from default configuration, reset here if in doubt)
\ 71 | \ 72 | \ 73 | \ 74 | \ 75 | \ 76 | \ 77 | \ 78 |
Type:Station (Connect to infrastructure)
AP (Broadcast a new AP)
SSID:
PASS:
MAC: (Ignored in softAP mode)
Chan: (Ignored in Station mode)
(Automatically saves to flash)
\ 79 | Scanned Stations: \ 80 |
\ 81 | \ 82 |
\ 83 | \ 84 | \ 85 | \ 86 |
\ 87 |
\ 88 | Command: \ 89 |
\ 90 | \ 91 |
\ 92 | \ 93 | \ 94 | \ 95 |
\ 96 | " + 97 | GPIOlines 98 | + "
\ 99 | \ 100 | \ 101 | \ 102 |
\ 103 |
\ 104 |
Drop or browse for system (0x000.. 0x400...) or web (.mpfs) reflash files.
\ 105 |
" 106 | ); 107 | 108 | MakeDragDrop( "InnerSystemReflash", DragDropSystemFiles ); 109 | $("#dragndropersystem").change(function() { DragDropSystemFiles(this.files ); }); 110 | 111 | $( ".collapsible" ).each(function( index ) { 112 | if( localStorage["sh" + this.id] > 0.5 ) 113 | { 114 | $( this ).show().toggleClass( 'opened' ); 115 | // console.log( "OPEN: " + this.id ); 116 | } 117 | }); 118 | 119 | $("#custom_command_response").val( "" ); 120 | 121 | //Preclude drag and drop on rest of document in event user misses firmware boxes. 122 | donothing = function(e) {e.stopPropagation();e.preventDefault();}; 123 | $(document).on('drop', donothing ); 124 | $(document).on('dragover', donothing ); 125 | $(document).on('dragenter', donothing ); 126 | 127 | output = document.getElementById("output"); 128 | 129 | KickWifiTicker(); 130 | GPIODataTickerStart(); 131 | InitSystemTicker(); 132 | 133 | console.log( "Load complete.\n" ); 134 | Ticker(); 135 | } 136 | 137 | window.addEventListener("load", init, false); 138 | 139 | 140 | function StartWebSocket() 141 | { 142 | output.innerHTML = "Connecting..."; 143 | if( websocket ) websocket.close(); 144 | workarray = {}; 145 | workqueue = []; 146 | lastitem = null; 147 | websocket = new WebSocket(wsUri); 148 | websocket.onopen = function(evt) { onOpen(evt) }; 149 | websocket.onclose = function(evt) { onClose(evt) }; 150 | websocket.onmessage = function(evt) { onMessage(evt) }; 151 | websocket.onerror = function(evt) { onError(evt) }; 152 | } 153 | 154 | function onOpen(evt) 155 | { 156 | doSend('e' ); 157 | } 158 | 159 | function onClose(evt) 160 | { 161 | $('#SystemStatusClicker').css("color", "red" ); 162 | commsup = 0; 163 | } 164 | 165 | var msg = 0; 166 | var tickmessage = 0; 167 | var lasthz = 0; 168 | var time_since_hz = 10; //Make it realize it was disconnected to begin with. 169 | 170 | function Ticker() 171 | { 172 | setTimeout( Ticker, 1000 ); 173 | 174 | lasthz = (msg - tickmessage); 175 | tickmessage = msg; 176 | if( lasthz == 0 ) 177 | { 178 | time_since_hz++; 179 | if( time_since_hz > 3 ) 180 | { 181 | $('#SystemStatusClicker').css("color", "red" ); 182 | $('#SystemStatusClicker').prop( "value", "System Offline" ); 183 | if( commsup != 0 && !is_waiting_on_stations ) IssueSystemMessage( "Comms Lost." ); 184 | commsup = 0; 185 | StartWebSocket(); 186 | } 187 | else 188 | $('#SystemStatusClicker').prop( "value", "System " + 0 + "Hz" ); 189 | } 190 | else 191 | { 192 | time_since_hz = 0; 193 | $('#SystemStatusClicker').prop( "value", "System " + lasthz + "Hz" ); 194 | } 195 | } 196 | 197 | 198 | function onMessage(evt) 199 | { 200 | msg++; 201 | 202 | 203 | if( commsup != 1 ) 204 | { 205 | commsup = 1; 206 | $('#SystemStatusClicker').css("color", "green" ); 207 | IssueSystemMessage( "Comms Established." ); 208 | } 209 | 210 | 211 | if( lastitem ) 212 | { 213 | if( lastitem.callback ) 214 | { 215 | lastitem.callback( lastitem, evt.data ); 216 | lastitem = null; 217 | } 218 | } 219 | else 220 | { 221 | if( evt.data.length > 2 ) 222 | { 223 | var wxresp = evt.data.substr(2).split("\t"); 224 | output.innerHTML = "

Messages: " + msg + "

RSSI: " + wxresp[0] + " / IP: " + ((wxresp.length>1)?HexToIP( wxresp[1] ):"") + "

"; 225 | } 226 | } 227 | 228 | 229 | if( workqueue.length ) 230 | { 231 | var elem = workqueue.shift(); 232 | delete workarray[elem.request]; 233 | 234 | if( elem.request ) 235 | { 236 | doSend( elem.request ); 237 | lastitem = elem; 238 | return; 239 | } 240 | } 241 | 242 | doSend('wx'); //Request RSSI. 243 | } 244 | 245 | function onError(evt) 246 | { 247 | $('#SystemStatusClicker').css("color", "red" ); 248 | commsup = 0; 249 | } 250 | 251 | function doSend(message) 252 | { 253 | websocket.send(message); 254 | } 255 | 256 | function IsTabOpen( objname ) 257 | { 258 | var obj = $( "#" + objname ); 259 | var opened = obj.is( '.opened' ); 260 | return opened != 0; 261 | } 262 | 263 | function ShowHideEvent( objname ) 264 | { 265 | var obj = $( "#" + objname ); 266 | obj.slideToggle( 'fast' ).toggleClass( 'opened' ); 267 | var opened = obj.is( '.opened' ); 268 | localStorage["sh" + objname] = opened?1:0; 269 | return opened!=0; 270 | } 271 | 272 | 273 | function IssueCustomCommand() 274 | { 275 | QueueOperation( $("#custom_command").val(), function( req,data) { $("#custom_command_response").val( data ); } ); 276 | } 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | function MakeDragDrop( divname, callback ) 288 | { 289 | var obj = $("#" + divname); 290 | obj.on('dragenter', function (e) 291 | { 292 | e.stopPropagation(); 293 | e.preventDefault(); 294 | $(this).css('border', '2px solid #0B85A1'); 295 | }); 296 | 297 | obj.on('dragover', function (e) 298 | { 299 | e.stopPropagation(); 300 | e.preventDefault(); 301 | }); 302 | 303 | obj.on('dragend', function (e) 304 | { 305 | e.stopPropagation(); 306 | e.preventDefault(); 307 | $(this).css('border', '2px dotted #0B85A1'); 308 | }); 309 | 310 | obj.on('drop', function (e) 311 | { 312 | $(this).css('border', '2px dotted #0B85A1'); 313 | e.preventDefault(); 314 | var files = e.originalEvent.dataTransfer.files; 315 | 316 | //We need to send dropped files to Server 317 | callback(files); 318 | }); 319 | } 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////// 328 | ///Below here are mostly just events... 329 | 330 | var sysset = null; 331 | var snchanged = false; 332 | var sdchanged = false; 333 | 334 | var lastpeerdata = ""; 335 | 336 | function CallbackForPeers(req,data) 337 | { 338 | if( data == lastpeerdata ) return; 339 | lastpeerdata = data; 340 | var lines = data.split( "\n" ); 341 | var searchcount = 0; 342 | if( lines.length > 0 ) 343 | { 344 | var line1 = lines[0].split( "\t" ); 345 | if( line1.length > 1 ) searchcount = Number( line1[1] ); 346 | } 347 | 348 | var htm = ""; 349 | for( var i = 1; i < lines.length; i++ ) 350 | { 351 | var elems = lines[i].split( "\t" ); 352 | if( elems.length < 4 ) continue; 353 | IP = HexToIP( elems[0] ); 354 | 355 | htm += ""; 356 | } 357 | htm += "
AddressServiceNameDescription
" + IP + "" + elems[1] + "" + elems[2] + "" + elems[3] + "
"; 358 | if( searchcount == 0 ) 359 | { 360 | htm += ""; 361 | } 362 | 363 | $("#peers").html( htm ); 364 | } 365 | 366 | function SysTickBack(req,data) 367 | { 368 | var params = data.split( "\t" ); 369 | if( !snchanged ) 370 | { 371 | $("#SystemName").prop( "value", params[3] ); 372 | $("#SystemName").removeClass( "unsaved-input"); 373 | } 374 | if( !sdchanged ) 375 | { 376 | $("#SystemDescription").prop( "value", params[4] ); 377 | $("#SystemDescription").removeClass( "unsaved-input"); 378 | } 379 | $("#ServiceName").html( params[5] ); 380 | $("#FreeHeap").html( params[6] ); 381 | 382 | QueueOperation( "BL", CallbackForPeers ); 383 | } 384 | 385 | function SystemInfoTick() 386 | { 387 | if( IsTabOpen('SystemStatus') ) 388 | { 389 | QueueOperation( "I", SysTickBack ); 390 | setTimeout( SystemInfoTick, 500 ); 391 | } 392 | else 393 | { 394 | //Stop. 395 | } 396 | } 397 | 398 | function SystemChangesReset() 399 | { 400 | snchanged = false; 401 | sdchanged = false; 402 | } 403 | 404 | function SystemUncommittedChanges() 405 | { 406 | if( sdchanged || snchanged ) return true; 407 | else return false; 408 | } 409 | 410 | function InitSystemTicker() 411 | { 412 | sysset = document.getElementById( "systemsettings" ); 413 | SystemInfoTick(); 414 | sysset.innerHTML = "\ 415 |
System Name:
System Description:
Service Name:
Free Heap:
\ 416 | \ 417 | \ 418 | \ 419 | \ 420 | \ 421 |

Search for others:

\ 422 |
"; 423 | $("#SystemName").on("input propertychange paste",function(){snchanged = true; $("#SystemName").addClass( "unsaved-input"); }); 424 | $("#SystemDescription").on("input propertychange paste",function(){sdchanged = true;$("#SystemDescription").addClass( "unsaved-input"); }); 425 | } 426 | 427 | 428 | 429 | did_wifi_get_config = false; 430 | is_data_ticker_running = false; 431 | is_waiting_on_stations = false; 432 | 433 | function ScanForWifi() 434 | { 435 | QueueOperation('WS', null); 436 | is_waiting_on_stations=true; 437 | IssueSystemMessage( "Scanning for Wifi..." ); 438 | } 439 | 440 | function KickWifiTicker() 441 | { 442 | if( !is_data_ticker_running ) 443 | WifiDataTicker(); 444 | } 445 | 446 | function BSSIDClick( i ) 447 | { 448 | var tlines = wifilines[i]; 449 | document.wifisection.wifitype.value = 1; 450 | document.wifisection.wificurname.value = tlines[0].substr(1); 451 | document.wifisection.wificurpassword.value = ""; 452 | document.wifisection.wifimac.value = tlines[1]; 453 | document.wifisection.wificurchannel.value = 0; 454 | 455 | ClickOpmode( 1 ); 456 | return false; 457 | } 458 | 459 | function ClickOpmode( i ) 460 | { 461 | if( i == 1 ) 462 | { 463 | document.wifisection.wificurname.disabled = false; 464 | document.wifisection.wificurpassword.disabled = false; 465 | document.wifisection.wifimac.disabled = false; 466 | document.wifisection.wificurchannel.disabled = true; 467 | } 468 | else 469 | { 470 | document.wifisection.wificurname.disabled = false; 471 | document.wifisection.wificurpassword.disabled = true; 472 | document.wifisection.wificurpassword.value = ""; 473 | document.wifisection.wifimac.disabled = true; 474 | document.wifisection.wificurchannel.disabled = false; 475 | } 476 | } 477 | 478 | function WifiDataTicker() 479 | { 480 | if( IsTabOpen('WifiSettings') ) 481 | { 482 | is_data_ticker_running = true; 483 | 484 | if( !did_wifi_get_config ) 485 | { 486 | QueueOperation( "WI", function(req,data) 487 | { 488 | var params = data.split( "\t" ); 489 | 490 | var opmode = Number( params[0].substr(2) ); 491 | document.wifisection.wifitype.value = opmode; 492 | document.wifisection.wificurname.value = params[1]; 493 | document.wifisection.wificurpassword.value = params[2]; 494 | document.wifisection.wifimac.value = params[3]; 495 | document.wifisection.wificurchannel.value = Number( params[4] ); 496 | 497 | ClickOpmode( opmode ); 498 | did_wifi_get_config = true; 499 | } ); 500 | } 501 | 502 | QueueOperation( "WR", function(req,data) { 503 | var lines = data.split( "\n" ); 504 | var innerhtml; 505 | if( data[0] == '!' ) return; //If no APs, don't deal with list. 506 | 507 | if( lines.length < 3 ) 508 | { 509 | innerhtml = "No APs found. Did you scan?"; 510 | if( is_waiting_on_stations ) 511 | { 512 | IssueSystemMessage( "No APs found." ); 513 | is_waiting_on_stations = false; 514 | } 515 | } 516 | else 517 | { 518 | if( is_waiting_on_stations ) 519 | { 520 | IssueSystemMessage( "Scan Complete." ); 521 | is_waiting_on_stations = false; 522 | } 523 | 524 | innerhtml = "" 525 | wifilines = []; 526 | for( i = 1; i < lines.length-1; i++ ) 527 | { 528 | tlines = lines[i].split( "\t" ); 529 | wifilines.push(tlines); 530 | var bssidval = "" + tlines[1]; 531 | innerhtml += ""; 532 | } 533 | } 534 | innerhtml += "
SSIDMACRSChEnc
" + tlines[0].substr(1) + "" + bssidval + "" + tlines[2] + "" + tlines[3] + "" + tlines[4] + "
"; 535 | document.getElementById("WifiStations").innerHTML = innerhtml; 536 | } ); 537 | setTimeout( WifiDataTicker, 500 ); 538 | } 539 | else 540 | { 541 | is_data_ticker_running = 0; 542 | } 543 | } 544 | 545 | function ChangeWifiConfig() 546 | { 547 | 548 | var st = "W"; 549 | st += document.wifisection.wifitype.value; 550 | st += "\t" + document.wifisection.wificurname.value; 551 | st += "\t" + document.wifisection.wificurpassword.value; 552 | st += "\t" + document.wifisection.wifimac.value; 553 | st += "\t" + document.wifisection.wificurchannel.value; 554 | QueueOperation( st ); 555 | did_wifi_get_config = false; 556 | } 557 | 558 | 559 | 560 | 561 | function TwiddleGPIO( gp ) 562 | { 563 | var st = "GF"; 564 | st += gp; 565 | QueueOperation( st ); 566 | } 567 | 568 | function GPIOInput( gp ) 569 | { 570 | var st = "GI"; 571 | st += gp; 572 | QueueOperation( st ); 573 | } 574 | 575 | function GPIOUpdate(req,data) { 576 | var secs = data.split( "\t" ); 577 | var op = 0; 578 | var n = Number(secs[2]); 579 | var m = Number(secs[1]); 580 | 581 | for( op = 0; op < 16; op++ ) 582 | { 583 | var b = $( "#ButtonGPIO" + op ); 584 | if( b ) 585 | { 586 | if( 1< 65536 ) 748 | { 749 | $("#innersystemflashtext").html( "0x00000 needs to fit in IRAM. Too big." ); return; 750 | } 751 | 752 | if( file2.size > 262144 ) 753 | { 754 | $("#innersystemflashtext").html( "0x40000 needs to fit in 256kB. Too big." ); return; 755 | } 756 | 757 | //Files check out. Start pushing. 758 | 759 | $("#innersystemflashtext").html( "Starting." ); 760 | 761 | var reader = new FileReader(); 762 | 763 | reader.onload = function(e) { 764 | var ctx = new Object(); 765 | ctx.file1 = file1; 766 | ctx.file2 = file2; 767 | ctx.current_state = 0; 768 | PushImageTo( e.target.result, flash_scratchpad_at, SystemPushImageProgress, ctx ); 769 | } 770 | 771 | reader.readAsArrayBuffer( file[0] ); 772 | return; 773 | } 774 | else 775 | { 776 | $("#innersystemflashtext").html( "Cannot accept anything other than 1 or 2 files." ); 777 | } 778 | } 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | function tohex8( c ) 789 | { 790 | var hex = c.toString(16); 791 | return hex.length == 1 ? "0" + hex : hex; 792 | } 793 | 794 | 795 | function HexToIP( hexstr ) 796 | { 797 | if( !hexstr ) return ""; 798 | return parseInt( hexstr.substr( 6, 2 ), 16 ) + "." + 799 | parseInt( hexstr.substr( 4, 2 ), 16 ) + "." + 800 | parseInt( hexstr.substr( 2, 2 ), 16 ) + "." + 801 | parseInt( hexstr.substr( 0, 2 ), 16 ); 802 | } 803 | 804 | function ContinueSystemFlash( fsrd, flashresponse, pushop ) 805 | { 806 | if( flashresponse[0] == '!' ) 807 | { 808 | pushop.status_callback( 0, flashresponse, pushop ); 809 | console.log( flashresponse ); 810 | return; 811 | } 812 | 813 | var cont = pushop.status_callback( 1, flashresponse, pushop ); 814 | 815 | if( !cont ) return; 816 | if( pushop.place >= pushop.padlen ) return; 817 | 818 | //If we are coming from a write, and now we need to erase the next block, do so. 819 | 820 | if( ( pushop.place % flash_blocksize ) == 0 && flashresponse[1] != 'B' ) 821 | { 822 | QueueOperation( "FB" + ((pushop.place+pushop.base_address)/flash_blocksize), function( x, y ) { ContinueSystemFlash( x, y, pushop ); } ); 823 | } 824 | else //Done erasing the next block, or coming off a write we don't need to erase? 825 | { 826 | var addy = pushop.place + pushop.base_address; 827 | var sendstr = "FX" + addy + "\t" + flash_sendsize + "\t"; 828 | for( var i = 0; i < flash_sendsize; i++ ) 829 | { 830 | sendstr += tohex8( pushop.paddata[pushop.place++] ); 831 | } 832 | QueueOperation( sendstr, function( x, y ) { ContinueSystemFlash( x, y, pushop ); } ); 833 | } 834 | } 835 | 836 | //The signature for status callback is: function AVRStatusCallback( is_ok, comment, pushop ) 837 | //If pushop.place == pushop.padlen, no further callbacks will continue, even if true is returned. 838 | //you must return "true." Returning false will cease further pushing. 839 | //This function returns an object with all properties about the transfer. 840 | //WARNING: "location" must be block (65536) aligned. 841 | function PushImageTo( arraydata, location, status_callback, ctx ) 842 | { 843 | if( location & 0xffff != 0 ) 844 | { 845 | console.log( "Error: to address not 65,536 aligned." ); 846 | return null; 847 | } 848 | 849 | var pushop = Object(); 850 | pushop.padlen = Math.floor(((arraydata.byteLength-1)/flash_sendsize)+1)*flash_sendsize; 851 | pushop.paddata = new Uint8Array( pushop.padlen, 0 ); 852 | pushop.paddata.set( new Uint8Array( arraydata ), 0 ); 853 | pushop.status_callback = status_callback; 854 | pushop.place = 0; 855 | pushop.base_address = location; 856 | pushop.ctx = ctx; 857 | 858 | ContinueSystemFlash( null, "Starting", pushop ); 859 | 860 | return pushop; 861 | } 862 | 863 | 864 | 865 | 866 | 867 | /* MD5 implementation minified from: http://blog.faultylabs.com/files/md5.js 868 | Javascript MD5 library - version 0.4 Coded (2011) by Luigi Galli - LG@4e71.org - http://faultylabs.com 869 | Thanks to: Roberto Viola The below code is PUBLIC DOMAIN - NO WARRANTY! 870 | */ 871 | "undefined"==typeof faultylabs&&(faultylabs={}),faultylabs.MD5=function(n){function r(n){var r=(n>>>0).toString(16);return"00000000".substr(0,8-r.length)+r}function t(n){for(var r=[],t=0;tt;t++)r.push(255&n),n>>>=8;return r}function o(n,r){return n<>>32-r}function a(n,r,t){return n&r|~n&t}function f(n,r,t){return t&n|~t&r}function u(n,r,t){return n^r^t}function i(n,r,t){return r^(n|~t)}function c(n,r){return n[r+3]<<24|n[r+2]<<16|n[r+1]<<8|n[r]}function s(n){for(var r=[],t=0;t=0;o--)e=arguments[o],t=255&e,e>>>=8,t<<=8,t|=255&e,e>>>=8,t<<=8,t|=255&e,e>>>=8,t<<=8,t|=e,n+=r(t);return n}function y(n){for(var r=new Array(n.length),t=0;t56){for(var s=0;64-t>s;s++)A.push(0);t=A.length%64}for(s=0;56-t>s;s++)A.push(0);A=A.concat(e(8*r));var y=1732584193,p=4023233417,g=2562383102,v=271733878,b=0,d=0,U=0,m=0;for(s=0;s< Charles Lohr Under the MIT/x11 License, NewBSD License or 3 | // ColorChord License. You Choose. 4 | 5 | #include 6 | #include 7 | #include 8 | #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) 9 | #include 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | struct libusb_device_handle *devh = NULL; 20 | 21 | #define USB_SIZE 128 22 | #define sector_SIZE 4096 23 | #ifndef NET_MAXTRIES 24 | #define NET_MAXTRIES 10 // In seconds, may be fractional 25 | #endif 26 | #ifndef NET_TIMEOUT 27 | #define NET_TIMEOUT 3.0 // In seconds, may be fractional 28 | #endif 29 | int sockfd; 30 | char recvline[10000]; 31 | 32 | int use_usb = 0; 33 | int sendsize_max = 1024; //For USB it will be smaller 34 | struct sockaddr_in servaddr,cliaddr; 35 | 36 | int SendData( uint8_t * buffer, int len ) 37 | { 38 | if( use_usb ) 39 | { 40 | int r1 = libusb_control_transfer( devh, 41 | 0x00, //reqtype (0x80 = Device->PC, 0x00 = PC->Device) 42 | 0xA6, //request 43 | 0x0100, //wValue 44 | 0x0000, //wIndex 45 | buffer, 46 | len, //wLength (more like max length) 47 | 1000 ); 48 | } 49 | else 50 | { 51 | return sendto( sockfd, buffer, len, MSG_NOSIGNAL, (struct sockaddr *)&servaddr,sizeof(servaddr)); 52 | } 53 | } 54 | 55 | 56 | int PushMatch( const char * match ) 57 | { 58 | if( use_usb ) 59 | { 60 | int tries = 0; 61 | for( tries = 0; tries < NET_MAXTRIES; tries++ ) 62 | { 63 | usleep( 1000 ); 64 | 65 | int r2 = libusb_control_transfer( devh, 66 | 0x80, //reqtype (0x80 = in, 0x00 = out) 67 | 0xA7, //request 68 | 0x0100, //wValue 69 | 0x0000, //wIndex 70 | recvline, //wLength (more like max length) 71 | USB_SIZE, 72 | 1000 ); 73 | 74 | if( r2 < 0 ) continue; 75 | 76 | recvline[r2] = 0; 77 | 78 | if( strncmp( recvline, match, strlen( match ) ) == 0 ) //XXX? Should this be memcmp? 79 | { 80 | return 0; 81 | } 82 | 83 | usleep( 20000 ); 84 | } 85 | return 1; 86 | } 87 | else 88 | { 89 | struct timeval tva, tvb; 90 | gettimeofday( &tva, 0 ); 91 | float diff = 0.0; 92 | while( diff < NET_TIMEOUT ) 93 | { 94 | struct pollfd ufds; 95 | ufds.fd = sockfd; 96 | ufds.events = POLLIN; 97 | int rv = poll(&ufds, 1, 500); 98 | if( rv > 0 ) 99 | { 100 | // tbuf = recvline; 101 | int n=recvfrom(sockfd,recvline,10000,0,NULL,NULL); 102 | if( strncmp( recvline, match, strlen( match ) ) == 0 ) 103 | { 104 | return 0; 105 | } 106 | } 107 | gettimeofday( &tvb, 0 ); 108 | diff = tvb.tv_sec - tva.tv_sec + 1e-6*(tvb.tv_usec - tva.tv_usec); 109 | } 110 | return 1; 111 | } 112 | } 113 | 114 | 115 | int main(int argc, char**argv) 116 | { 117 | int n; 118 | char sendline[1000]; 119 | // char recvline[1000]; 120 | 121 | if (argc != 4) 122 | { 123 | printf("usage: pushtodev [IP address] [address offset] [file]\n"); 124 | exit(-1); 125 | } 126 | 127 | int offset = atoi( argv[2] ); 128 | const char * file = argv[3]; 129 | 130 | if( offset <= 0 ) 131 | { 132 | fprintf( stderr, "Error: Cannot write to address 0 or before.\n" ); 133 | exit(-2); 134 | } 135 | 136 | FILE * f = fopen( file, "rb" ); 137 | if( !f || feof( f ) ) 138 | { 139 | fprintf( stderr, "Error: cannot open file.\n" ); 140 | exit(-3); 141 | } 142 | 143 | 144 | if( strcmp( argv[1], "USB" ) == 0 ) 145 | { 146 | int r; 147 | use_usb = 1; 148 | printf( "Connecting by USB\n" ); 149 | fprintf( stderr, "WARNING: USB BURNING IS EXPERIMENTAL AND LIKELY TO CHANGE\n" ); 150 | if( libusb_init(NULL) < 0 ) 151 | { 152 | fprintf( stderr, "Error: Could not initialize libUSB\n" ); 153 | return -1; 154 | } 155 | 156 | 157 | devh = libusb_open_device_with_vid_pid( NULL, 0xabcd, 0x8266 ); 158 | if( !devh ) 159 | { 160 | fprintf( stderr, "Error: Cannot find USB device.\n" ); 161 | return -1; 162 | } 163 | 164 | libusb_detach_kernel_driver( devh, 0 ); 165 | 166 | if( (r=libusb_claim_interface(devh, 0)) < 0 ) 167 | { 168 | fprintf(stderr, "usb_claim_interface error %d\n", r); 169 | return -1; 170 | } 171 | printf( "Connected.\n" ); 172 | //USB is attached 173 | sendsize_max = USB_SIZE; 174 | } 175 | else 176 | { 177 | sockfd=socket(AF_INET,SOCK_DGRAM,0); 178 | 179 | bzero(&servaddr,sizeof(servaddr)); 180 | servaddr.sin_family = AF_INET; 181 | servaddr.sin_addr.s_addr=inet_addr(argv[1]); 182 | servaddr.sin_port=htons(BACKEND_PORT); 183 | } 184 | 185 | int devo = 0; 186 | int lastsector_block = -1; 187 | int resend_times = 0; 188 | int r; 189 | while( !feof( f ) ) 190 | { 191 | int tries; 192 | int thissuccess; 193 | char buffer[1024]; 194 | char bufferout[1600]; 195 | int reads = fread( buffer, 1, sendsize_max, f ); 196 | int sendplace = offset + devo; 197 | int sendsize = reads; 198 | if( sendsize == 0 ) break; 199 | 200 | #ifdef SECTOR 201 | int sector = sendplace / sector_SIZE; 202 | if( sector != lastsector_block ) 203 | { 204 | char se[64]; 205 | int sel = sprintf( se, "FE%d\r\n", sector ); 206 | 207 | thissuccess = 0; 208 | for( tries = 0; tries < NET_MAXTRIES; tries++ ) 209 | { 210 | char match[75]; 211 | printf( "Erase: %d\n", sector ); 212 | SendData( se, sel ); 213 | sprintf( match, "FE%d", sector ); 214 | 215 | if( PushMatch(match) == 0 ) { thissuccess = 1; break; } 216 | printf( "Retry.\n" ); 217 | } 218 | if( !thissuccess ) 219 | { 220 | fprintf( stderr, "Error: Timeout in communications.\n" ); 221 | exit( -6 ); 222 | } 223 | 224 | lastsector_block = sector; 225 | } 226 | #else //block 227 | 228 | int block = sendplace / 65536; 229 | if( block != lastsector_block ) 230 | { 231 | char se[64]; 232 | int sel = sprintf( se, "FB%d\r\n", block ); 233 | 234 | thissuccess = 0; 235 | for( tries = 0; tries < 10; tries++ ) 236 | { 237 | char match[75]; 238 | printf( "Erase: %d\n", block ); 239 | SendData( se, sel ); 240 | sprintf( match, "FB%d", block ); 241 | 242 | if( PushMatch(match) == 0 ) { thissuccess = 1; break; } 243 | printf( "Retry.\n" ); 244 | } 245 | if( !thissuccess ) 246 | { 247 | fprintf( stderr, "Error: Timeout in communications.\n" ); 248 | exit( -6 ); 249 | } 250 | 251 | lastsector_block = block; 252 | } 253 | #endif 254 | resend_times = 0; 255 | resend: 256 | r = sprintf( bufferout, "FW%d\t%d\t", sendplace, sendsize ); 257 | //printf( "bufferout: %d %d %s\n", sendplace, sendsize, bufferout ); 258 | printf( "." ); fflush( stdout ); 259 | memcpy( bufferout + r, buffer, sendsize ); 260 | 261 | 262 | thissuccess = 0; 263 | for( tries = 0; tries < 10; tries++ ) 264 | { 265 | char match[75]; 266 | SendData( bufferout, reads + r ); 267 | sprintf( match, "FW%d", sendplace ); 268 | 269 | if( PushMatch(match) == 0 ) { thissuccess = 1; break; } 270 | printf( "Retry.\n" ); 271 | } 272 | if( !thissuccess ) 273 | { 274 | fprintf( stderr, "Error: Timeout in communications.\n" ); 275 | exit( -6 ); 276 | } 277 | 278 | /* 279 | printf( "Verifying..." ); 280 | fflush( stdout ); 281 | 282 | int r = sprintf( bufferout, "FR%d:%d", sendplace, sendsize ); 283 | bufferout[r] = 0; 284 | 285 | thissuccess = 0; 286 | for( tries = 0; tries < 10; tries++ ) 287 | { 288 | char match[1600]; 289 | sendto( sockfd, bufferout, r, MSG_NOSIGNAL, (struct sockaddr *)&servaddr,sizeof(servaddr)); 290 | devo += reads; 291 | sprintf( match, "FR%08d", sendplace ); 292 | 293 | if( PushMatch(match) == 0 ) { 294 | 295 | //Check data... 296 | //printf( "RR:%s\n", recvline ); 297 | char * colon1 = strchr( recvline, ':' ); 298 | char * colon2 = (colon1)?strchr( colon1+1, ':' ):0; 299 | //printf( "::%p %p \"%s\"\n", colon1, colon2,recvline ); 300 | if( colon2 ) 301 | { 302 | if( memcmp( colon2+1, buffer, sendsize ) == 0 ) 303 | thissuccess = 1; 304 | } 305 | 306 | if( !thissuccess ) 307 | { 308 | if( resend_times > 2 ) 309 | { 310 | break; 311 | } 312 | resend_times++; 313 | goto resend; 314 | } 315 | break; 316 | } 317 | printf( "Retry.\n" ); 318 | } 319 | if( !thissuccess ) 320 | { 321 | fprintf( stderr, "Error: Fault verifying.\n" ); 322 | exit( -6 ); 323 | } 324 | */ 325 | devo += reads; 326 | 327 | } 328 | 329 | printf( "Send done.\n" ); 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /hardware/ESPLocalizerDec2016.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnlohr/esplocalizer/f721be42459857843b51c5eaff5be4fd75728382/hardware/ESPLocalizerDec2016.JPG -------------------------------------------------------------------------------- /hardware/PinoutDesign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnlohr/esplocalizer/f721be42459857843b51c5eaff5be4fd75728382/hardware/PinoutDesign.png -------------------------------------------------------------------------------- /hardware/esplocalizer-tight-F-cache.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.3 2 | #encoding utf-8 3 | # 4 | # +3.3V 5 | # 6 | DEF +3.3V #PWR 0 0 Y Y 1 F P 7 | F0 "#PWR" 0 -40 30 H I C CNN 8 | F1 "+3.3V" 0 110 30 H V C CNN 9 | F2 "" 0 0 60 H V C CNN 10 | F3 "" 0 0 60 H V C CNN 11 | ALIAS +3,3V 12 | DRAW 13 | X +3.3V 1 0 0 0 U 30 30 0 0 W N 14 | C 0 60 20 0 1 0 N 15 | P 3 0 1 0 0 0 0 40 0 40 N 16 | ENDDRAW 17 | ENDDEF 18 | # 19 | # +3.3VP 20 | # 21 | DEF +3.3VP #PWR 0 0 Y Y 1 F P 22 | F0 "#PWR" 50 30 20 H I C CNN 23 | F1 "+3.3VP" 0 90 30 H V C CNN 24 | F2 "" 0 0 60 H V C CNN 25 | F3 "" 0 0 60 H V C CNN 26 | DRAW 27 | X +3.3VP 1 0 0 0 U 20 20 0 0 W N 28 | P 3 0 1 0 0 0 0 40 0 40 N 29 | P 7 0 1 0 20 30 0 40 -20 30 -10 70 10 70 20 30 20 30 N 30 | ENDDRAW 31 | ENDDEF 32 | # 33 | # 1206NETWORK 34 | # 35 | DEF 1206NETWORK U 0 40 Y Y 1 F N 36 | F0 "U" -300 400 60 H V C CNN 37 | F1 "1206NETWORK" 50 -200 60 H V C CNN 38 | F2 "" 0 0 60 H V C CNN 39 | F3 "" 0 0 60 H V C CNN 40 | $FPLIST 41 | 1206NETWORK 42 | $ENDFPLIST 43 | DRAW 44 | S -200 300 200 -100 0 1 0 f 45 | X R1 1 -500 250 300 R 50 50 1 1 I 46 | X R2 2 -500 150 300 R 50 50 1 1 I 47 | X R3 3 -500 50 300 R 50 50 1 1 I 48 | X R4 4 -500 -50 300 R 50 50 1 1 I 49 | X R4 5 500 -50 300 L 50 50 1 1 I 50 | X R3 6 500 50 300 L 50 50 1 1 I 51 | X R2 7 500 150 300 L 50 50 1 1 I 52 | X R1 8 500 250 300 L 50 50 1 1 I 53 | ENDDRAW 54 | ENDDEF 55 | # 56 | # BMP280 57 | # 58 | DEF BMP280 U 0 40 Y Y 1 F N 59 | F0 "U" 200 250 60 H V C CNN 60 | F1 "BMP280" -200 250 60 H V C CNN 61 | F2 "" 300 -150 60 H V C CNN 62 | F3 "" 300 -150 60 H V C CNN 63 | DRAW 64 | S -250 200 250 -200 0 1 0 f 65 | X GND 1 550 150 300 L 50 50 1 1 I 66 | X CSB 2 550 50 300 L 50 50 1 1 I 67 | X SDA 3 550 -50 300 L 50 50 1 1 I 68 | X SCK 4 550 -150 300 L 50 50 1 1 I 69 | X SDO 5 -550 -150 300 R 50 50 1 1 I 70 | X VddIO 6 -550 -50 300 R 50 50 1 1 I 71 | X GND 7 -550 50 300 R 50 50 1 1 I 72 | X Vdd 8 -550 150 300 R 50 50 1 1 I 73 | ENDDRAW 74 | ENDDEF 75 | # 76 | # C 77 | # 78 | DEF C C 0 10 N Y 1 F N 79 | F0 "C" 0 100 40 H V L CNN 80 | F1 "C" 6 -85 40 H V L CNN 81 | F2 "" 38 -150 30 H V C CNN 82 | F3 "" 0 0 60 H V C CNN 83 | $FPLIST 84 | SM* 85 | C? 86 | C1-1 87 | $ENDFPLIST 88 | DRAW 89 | P 2 0 1 20 -80 -30 80 -30 N 90 | P 2 0 1 20 -80 30 80 30 N 91 | X ~ 1 0 200 170 D 40 40 1 1 P 92 | X ~ 2 0 -200 170 U 40 40 1 1 P 93 | ENDDRAW 94 | ENDDEF 95 | # 96 | # CONN_1 97 | # 98 | DEF ~CONN_1 P 0 30 N N 1 F N 99 | F0 "P" 80 0 40 H V L CNN 100 | F1 "CONN_1" 0 55 30 H I C CNN 101 | F2 "" 0 0 60 H V C CNN 102 | F3 "" 0 0 60 H V C CNN 103 | DRAW 104 | C 0 0 31 0 1 0 N 105 | P 2 0 1 0 -30 0 -50 0 N 106 | X 1 1 -150 0 100 R 60 60 1 1 P 107 | ENDDRAW 108 | ENDDEF 109 | # 110 | # CONN_2 111 | # 112 | DEF CONN_2 P 0 40 Y N 1 F N 113 | F0 "P" -50 0 40 V V C CNN 114 | F1 "CONN_2" 50 0 40 V V C CNN 115 | F2 "" 0 0 60 H V C CNN 116 | F3 "" 0 0 60 H V C CNN 117 | DRAW 118 | S -100 150 100 -150 0 1 0 N 119 | X P1 1 -350 100 250 R 60 60 1 1 P I 120 | X PM 2 -350 -100 250 R 60 60 1 1 P I 121 | ENDDRAW 122 | ENDDEF 123 | # 124 | # CONN_4 125 | # 126 | DEF CONN_4 P 0 40 Y N 1 F N 127 | F0 "P" -50 0 50 V V C CNN 128 | F1 "CONN_4" 50 0 50 V V C CNN 129 | F2 "" 0 0 60 H V C CNN 130 | F3 "" 0 0 60 H V C CNN 131 | DRAW 132 | S -100 200 100 -200 0 1 0 N 133 | X P1 1 -350 150 250 R 50 50 1 1 P I 134 | X P2 2 -350 50 250 R 50 50 1 1 P I 135 | X P3 3 -350 -50 250 R 50 50 1 1 P I 136 | X P4 4 -350 -150 250 R 50 50 1 1 P I 137 | ENDDRAW 138 | ENDDEF 139 | # 140 | # CONN_6 141 | # 142 | DEF CONN_6 P 0 30 Y N 1 F N 143 | F0 "P" -50 0 60 V V C CNN 144 | F1 "CONN_6" 50 0 60 V V C CNN 145 | F2 "" 0 0 60 H V C CNN 146 | F3 "" 0 0 60 H V C CNN 147 | DRAW 148 | S -100 300 100 -300 0 1 0 N 149 | X 1 1 -350 250 250 R 60 60 1 1 P I 150 | X 2 2 -350 150 250 R 60 60 1 1 P I 151 | X 3 3 -350 50 250 R 60 60 1 1 P I 152 | X 4 4 -350 -50 250 R 60 60 1 1 P I 153 | X 5 5 -350 -150 250 R 60 60 1 1 P I 154 | X 6 6 -350 -250 250 R 60 60 1 1 P I 155 | ENDDRAW 156 | ENDDEF 157 | # 158 | # CONN_9 159 | # 160 | DEF CONN_9 P 0 40 Y N 1 F N 161 | F0 "P" -50 0 60 V V C CNN 162 | F1 "CONN_9" 50 0 60 V V C CNN 163 | F2 "" 0 0 60 H V C CNN 164 | F3 "" 0 0 60 H V C CNN 165 | DRAW 166 | S -100 450 100 -450 0 1 0 N 167 | X P1 1 -350 400 250 R 50 50 1 1 P I 168 | X P2 2 -350 300 250 R 50 50 1 1 P I 169 | X P3 3 -350 200 250 R 50 50 1 1 P I 170 | X P4 4 -350 100 250 R 50 50 1 1 P I 171 | X P5 5 -350 0 250 R 50 50 1 1 P I 172 | X P6 6 -350 -100 250 R 50 50 1 1 P I 173 | X P7 7 -350 -200 250 R 50 50 1 1 P I 174 | X P8 8 -350 -300 250 R 50 50 1 1 P I 175 | X P9 9 -350 -400 250 R 50 50 1 1 P I 176 | ENDDRAW 177 | ENDDEF 178 | # 179 | # DIODESCH 180 | # 181 | DEF DIODESCH D 0 40 N N 1 F N 182 | F0 "D" 0 100 40 H V C CNN 183 | F1 "DIODESCH" 0 -100 40 H V C CNN 184 | F2 "" 0 0 60 H V C CNN 185 | F3 "" 0 0 60 H V C CNN 186 | $FPLIST 187 | D? 188 | S* 189 | $ENDFPLIST 190 | DRAW 191 | P 3 0 1 0 -50 50 50 0 -50 -50 F 192 | P 6 0 1 8 75 25 75 50 50 50 50 -50 25 -50 25 -25 N 193 | X A 1 -200 0 150 R 40 40 1 1 P 194 | X K 2 200 0 150 L 40 40 1 1 P 195 | ENDDRAW 196 | ENDDEF 197 | # 198 | # ESP12E 199 | # 200 | DEF ESP12E ESP 0 40 Y Y 1 F N 201 | F0 "ESP" 300 600 60 H V C CNN 202 | F1 "ESP12E" -300 600 60 H V C CNN 203 | F2 "" 50 -250 60 H V C CNN 204 | F3 "" 50 -250 60 H V C CNN 205 | DRAW 206 | S -500 750 500 -450 0 1 0 f 207 | X REST 1 -800 450 300 R 50 50 1 1 I 208 | X ADC 2 -800 350 300 R 50 50 1 1 I 209 | X CH_PD 3 -800 250 300 R 50 50 1 1 I 210 | X GPIO16 4 -800 150 300 R 50 50 1 1 I 211 | X GPIO14 5 -800 50 300 R 50 50 1 1 I 212 | X GPIO12 6 -800 -50 300 R 50 50 1 1 I 213 | X GPIO13 7 -800 -150 300 R 50 50 1 1 I 214 | X VCC 8 -800 -250 300 R 50 50 1 1 I 215 | X MTDO 9 -250 -750 300 U 50 50 1 1 I 216 | X MTDI 10 -150 -750 300 U 50 50 1 1 I 217 | X GPIO5 20 800 250 300 L 50 50 1 1 I 218 | X SD_3 11 -50 -750 300 U 50 50 1 1 I 219 | X RXD 21 800 350 300 L 50 50 1 1 I 220 | X MTMS 12 50 -750 300 U 50 50 1 1 I 221 | X TXD 22 800 450 300 L 50 50 1 1 I 222 | X MTCK 13 150 -750 300 U 50 50 1 1 I 223 | X SD_2 14 250 -750 300 U 50 50 1 1 I 224 | X GND 15 800 -250 300 L 50 50 1 1 I 225 | X GPIO15 16 800 -150 300 L 50 50 1 1 I 226 | X GPIO2 17 800 -50 300 L 50 50 1 1 I 227 | X GPIO0 18 800 50 300 L 50 50 1 1 I 228 | X GPIO4 19 800 150 300 L 50 50 1 1 I 229 | ENDDRAW 230 | ENDDEF 231 | # 232 | # GND 233 | # 234 | DEF ~GND #PWR 0 0 Y Y 1 F P 235 | F0 "#PWR" 0 0 30 H I C CNN 236 | F1 "GND" 0 -70 30 H I C CNN 237 | F2 "" 0 0 60 H V C CNN 238 | F3 "" 0 0 60 H V C CNN 239 | DRAW 240 | P 4 0 1 0 -50 0 0 -50 50 0 -50 0 N 241 | X GND 1 0 0 0 U 30 30 1 1 W N 242 | ENDDRAW 243 | ENDDEF 244 | # 245 | # LED 246 | # 247 | DEF LED D 0 40 Y N 1 F N 248 | F0 "D" 0 100 50 H V C CNN 249 | F1 "LED" 0 -100 50 H V C CNN 250 | F2 "" 0 0 60 H V C CNN 251 | F3 "" 0 0 60 H V C CNN 252 | $FPLIST 253 | LED-3MM 254 | LED-5MM 255 | LED-10MM 256 | LED-0603 257 | LED-0805 258 | LED-1206 259 | LEDV 260 | $ENDFPLIST 261 | DRAW 262 | P 2 0 1 0 50 50 50 -50 N 263 | P 3 0 1 0 -50 50 50 0 -50 -50 F 264 | P 3 0 1 0 65 -40 110 -80 105 -55 N 265 | P 3 0 1 0 80 -25 125 -65 120 -40 N 266 | X A 1 -200 0 150 R 40 40 1 1 P 267 | X K 2 200 0 150 L 40 40 1 1 P 268 | ENDDRAW 269 | ENDDEF 270 | # 271 | # LSM9DS1 272 | # 273 | DEF LSM9DS1 U 0 40 Y Y 1 F N 274 | F0 "U" -500 -450 60 H V C CNN 275 | F1 "LSM9DS1" 500 500 60 H V C CNN 276 | F2 "" -650 450 60 H V C CNN 277 | F3 "" -650 450 60 H V C CNN 278 | $FPLIST 279 | QFN24 280 | $ENDFPLIST 281 | DRAW 282 | S -600 450 600 -400 0 1 0 f 283 | X VddIO 1 -800 250 300 R 50 50 1 1 I 284 | X SCL 2 -800 150 300 R 50 50 1 1 I 285 | X VddI 3 -800 50 300 R 50 50 1 1 I 286 | X SDA 4 -800 -50 300 R 50 50 1 1 I 287 | X A/G 5 -800 -150 300 R 50 50 1 1 I 288 | X M 6 -800 -250 300 R 50 50 1 1 I 289 | X CS_AG 7 -250 -600 300 U 50 50 1 1 I 290 | X CS_M 8 -150 -600 300 U 50 50 1 1 I 291 | X DRDY_M 9 -50 -600 300 U 50 50 1 1 I 292 | X INT_M 10 50 -600 300 U 50 50 1 1 I 293 | X RES 20 150 650 300 D 50 50 1 1 I 294 | X INT_AG 11 150 -600 300 U 50 50 1 1 I 295 | X CAP 21 50 650 300 D 50 50 1 1 I 296 | X INT2AG 12 250 -600 300 U 50 50 1 1 I 297 | X Vdd 22 -50 650 300 D 50 50 1 1 I 298 | X DENA 13 800 -250 300 L 50 50 1 1 I 299 | X Vdd 23 -150 650 300 D 50 50 1 1 I 300 | X RES 14 800 -150 300 L 50 50 1 1 I 301 | X C1 24 -250 650 300 D 50 50 1 1 I 302 | X RES 15 800 -50 300 L 50 50 1 1 I 303 | X RES 16 800 50 300 L 50 50 1 1 I 304 | X RES 17 800 150 300 L 50 50 1 1 I 305 | X RES 18 800 250 300 L 50 50 1 1 I 306 | X RES 19 250 650 300 D 50 50 1 1 I 307 | ENDDRAW 308 | ENDDEF 309 | # 310 | # MCP1824 311 | # 312 | DEF MCP1824 U 0 40 Y Y 1 F N 313 | F0 "U" -250 300 60 H V C CNN 314 | F1 "MCP1824" 100 -300 60 H V C CNN 315 | F2 "" 0 0 60 H V C CNN 316 | F3 "" 0 0 60 H V C CNN 317 | $FPLIST 318 | SOT23-5 319 | $ENDFPLIST 320 | DRAW 321 | S -300 200 300 -200 0 1 0 f 322 | X Vin 1 -600 100 300 R 50 50 1 1 I 323 | X GND 2 -600 0 300 R 50 50 1 1 I 324 | X ~SHDN 3 -600 -100 300 R 50 50 1 1 I 325 | X PWRGD 4 600 -100 300 L 50 50 1 1 O 326 | X OUT 5 600 100 300 L 50 50 1 1 O 327 | ENDDRAW 328 | ENDDEF 329 | # 330 | # R 331 | # 332 | DEF R R 0 0 N Y 1 F N 333 | F0 "R" 80 0 40 V V C CNN 334 | F1 "R" 7 1 40 V V C CNN 335 | F2 "" -70 0 30 V V C CNN 336 | F3 "" 0 0 30 H V C CNN 337 | $FPLIST 338 | R? 339 | SM0603 340 | SM0805 341 | R?-* 342 | SM1206 343 | $ENDFPLIST 344 | DRAW 345 | S -40 150 40 -150 0 1 12 N 346 | X ~ 1 0 250 100 D 60 60 1 1 P 347 | X ~ 2 0 -250 100 U 60 60 1 1 P 348 | ENDDRAW 349 | ENDDEF 350 | # 351 | # STBC08-BATTCHARGER 352 | # 353 | DEF STBC08-BATTCHARGER U 0 40 Y Y 1 F N 354 | F0 "U" 0 100 60 H V C CNN 355 | F1 "STBC08-BATTCHARGER" 50 200 60 H V C CNN 356 | F2 "" 0 0 60 H V C CNN 357 | F3 "" 0 0 60 H V C CNN 358 | $FPLIST 359 | DFN6 360 | $ENDFPLIST 361 | DRAW 362 | S -300 150 300 -150 0 1 0 f 363 | X BAT 1 -450 100 157 R 50 50 1 1 I 364 | X PWON 2 -450 0 157 R 50 50 1 1 I 365 | X CHRG 3 -450 -100 157 R 50 50 1 1 I 366 | X GND 4 450 -100 157 L 50 50 1 1 I 367 | X PROG 5 450 0 157 L 50 50 1 1 I 368 | X Vcc 6 450 100 157 L 50 50 1 1 I 369 | X PAD 7 0 -300 157 U 50 50 1 1 I 370 | ENDDRAW 371 | ENDDEF 372 | # 373 | # SWITCH_INV 374 | # 375 | DEF SWITCH_INV SW 0 0 N Y 1 F N 376 | F0 "SW" -200 150 50 H V C CNN 377 | F1 "SWITCH_INV" -150 -150 50 H V C CNN 378 | F2 "" 0 0 60 H V C CNN 379 | F3 "" 0 0 60 H V C CNN 380 | DRAW 381 | C -150 0 50 0 0 0 N 382 | C 150 -100 50 0 0 0 N 383 | C 150 100 50 0 1 0 N 384 | P 2 0 1 0 -100 0 150 50 N 385 | X 1 1 500 100 300 L 60 60 1 1 P 386 | X 2 2 -500 0 300 R 60 60 1 1 P 387 | X 3 3 500 -100 300 L 60 60 1 1 P 388 | ENDDRAW 389 | ENDDEF 390 | # 391 | # TACTILE 392 | # 393 | DEF TACTILE SW 0 40 Y Y 1 F N 394 | F0 "SW" -450 100 60 H V C CNN 395 | F1 "TACTILE" -450 0 60 H V C CNN 396 | F2 "" 0 100 60 H V C CNN 397 | F3 "" 0 100 60 H V C CNN 398 | DRAW 399 | C 0 50 0 0 1 0 N 400 | C 0 50 0 0 1 0 N 401 | S -200 200 200 -200 0 1 0 f 402 | S 50 100 100 -100 0 1 0 N 403 | P 2 0 1 0 -150 150 150 150 N 404 | P 2 0 1 0 0 -150 0 -50 N 405 | P 2 0 1 0 0 100 0 50 N 406 | P 2 0 1 0 0 150 0 100 N 407 | P 3 0 1 0 150 -150 0 -150 -150 -150 N 408 | X ~ 1 -150 450 300 D 50 50 1 1 I 409 | X ~ 2 150 450 300 D 50 50 1 1 I 410 | X ~ 3 -150 -450 300 U 50 50 1 1 I 411 | X ~ 4 150 -450 300 U 50 50 1 1 I 412 | ENDDRAW 413 | ENDDEF 414 | # 415 | #End Library 416 | -------------------------------------------------------------------------------- /hardware/esplocalizer-tight-F.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnlohr/esplocalizer/f721be42459857843b51c5eaff5be4fd75728382/hardware/esplocalizer-tight-F.pdf --------------------------------------------------------------------------------