From 271c7de2553b0f49464bdd25cf91856a69785d72 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 26 Jan 2020 10:28:37 +0100 Subject: [PATCH] Framework code --- .gitignore | 3 +- DeskControl.sublime-project | 3 +- README.md | 20 +++++++ UNLICENSE | 24 +++++++++ build.ps1 | 1 - doc/DeskControl UI mockup - LCD version.psd | Bin 0 -> 98492 bytes mount/Mount.factory | Bin 0 -> 34055 bytes platformio.ini | 3 ++ src/include/display.h | 14 +++++ src/include/heightsensor.h | 6 +++ src/lib/menu.cpp | 7 +++ src/lib/menu.h | 25 +++++++++ src/lib/vl53l0x.cpp | 56 ++++++++++++++++++++ src/lib/vl53l0x.h | 37 +++++++++++++ src/main.cpp | 47 +++++++++++++++- upload.ps1 | 1 - 16 files changed, 240 insertions(+), 7 deletions(-) create mode 100644 README.md create mode 100644 UNLICENSE delete mode 100644 build.ps1 create mode 100644 doc/DeskControl UI mockup - LCD version.psd create mode 100644 mount/Mount.factory create mode 100644 src/include/display.h create mode 100644 src/include/heightsensor.h create mode 100644 src/lib/menu.cpp create mode 100644 src/lib/menu.h create mode 100644 src/lib/vl53l0x.cpp create mode 100644 src/lib/vl53l0x.h delete mode 100644 upload.ps1 diff --git a/.gitignore b/.gitignore index a974556..eb635d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.pioenvs -.piolibdeps +.pio *.kicad_pcb-bak *.sublime-workspace \ No newline at end of file diff --git a/DeskControl.sublime-project b/DeskControl.sublime-project index b01586b..c8ddc3f 100644 --- a/DeskControl.sublime-project +++ b/DeskControl.sublime-project @@ -3,7 +3,8 @@ [ { "path": ".", - "file_exclude_patterns": ["*.sublime-project"] + "file_exclude_patterns": ["*.sublime-project"], + "folder_exclude_patterns": [".pioenvs"] } ] } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e0416ba --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# DeskControl +This project is for motorizing a (very specific) manually controlled standing desk. I wanted to call it AutoDesk, but I don't like getting sued :-) + +Contained in this repository are the hardware designs and software source code. I doubt it's useful to anyone but me, but it may contain some interesting bits of code. + +All contents are released under the [Unlicense](https://unlicense.org/). Help yourself! + +## Building and uploading +This project uses [PlatformIO](https://platformio.org/). For reference, here are some of the common commands: + + + +_Building_ +```pio run``` + +_Building and uploading_ +```pio run -t upload``` + +_Installing the libraries (without building)_ +```pio lib install``` diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..00d2e13 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to \ No newline at end of file diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index c2fb43d..0000000 --- a/build.ps1 +++ /dev/null @@ -1 +0,0 @@ -& platformio run \ No newline at end of file diff --git a/doc/DeskControl UI mockup - LCD version.psd b/doc/DeskControl UI mockup - LCD version.psd new file mode 100644 index 0000000000000000000000000000000000000000..45aac42935d60b73ca08f8fbb86bfc48588cd8d9 GIT binary patch literal 98492 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 literal 0 HcmV?d00001 diff --git a/platformio.ini b/platformio.ini index 9a40e91..4a39861 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,3 +21,6 @@ board_build.f_cpu = 8000000L lib_deps = Bounce2 + Adafruit GFX Library + Adafruit ST7735 and ST7789 Library + Adafruit_VL53L0X \ No newline at end of file diff --git a/src/include/display.h b/src/include/display.h new file mode 100644 index 0000000..4aeec71 --- /dev/null +++ b/src/include/display.h @@ -0,0 +1,14 @@ +#ifndef __ports +#define __ports + +// Settings for an ST7789 based 240x240 pixel LCD. +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 240 +#define DISPLAY_ROTATION 2 + +// Control pins. Data is sent over the hardware SPI pins. +#define DISPLAY_PORT_CS 10 +#define DISPLAY_PORT_RST 9 +#define DISPLAY_PORT_DC 8 + +#endif \ No newline at end of file diff --git a/src/include/heightsensor.h b/src/include/heightsensor.h new file mode 100644 index 0000000..ccc9abc --- /dev/null +++ b/src/include/heightsensor.h @@ -0,0 +1,6 @@ +#ifndef __heightsensor +#define __heightsensor + +#define HEIGHTSENSOR_I2C_ADDRESS 0x29 + +#endif \ No newline at end of file diff --git a/src/lib/menu.cpp b/src/lib/menu.cpp new file mode 100644 index 0000000..8ca49cd --- /dev/null +++ b/src/lib/menu.cpp @@ -0,0 +1,7 @@ +#include "./menu.h" +#include + +void Menu::init() +{ + this->display->setFont(&FreeSansBold18pt7b); +} \ No newline at end of file diff --git a/src/lib/menu.h b/src/lib/menu.h new file mode 100644 index 0000000..6d6e1e6 --- /dev/null +++ b/src/lib/menu.h @@ -0,0 +1,25 @@ +#ifndef __menu +#define __menu + +#include +#include "include/display.h" + +#if DISPLAY_WIDTH != 240 || DISPLAY_HEIGHT != 240 + #error "The menu assumes a display pixel size of 240x240" +#endif + +class Menu +{ + private: + Adafruit_GFX* display; + + public: + Menu(Adafruit_GFX* display) + { + this->display = display; + } + + void init(); +}; + +#endif \ No newline at end of file diff --git a/src/lib/vl53l0x.cpp b/src/lib/vl53l0x.cpp new file mode 100644 index 0000000..86e3e02 --- /dev/null +++ b/src/lib/vl53l0x.cpp @@ -0,0 +1,56 @@ +#include "VL53L0X.h" + +bool VL53L0X::init(uint8_t address) +{ + device.I2cDevAddr = address; + device.comms_type = 1; + device.comms_speed_khz = 400; + device.i2c = &Wire; + + if (!VL53L0X_DataInit(&device)) + return false; + + //if (!VL53L0X_GetDeviceInfo(&device, &deviceInfo)) + // return false; + + if (!VL53L0X_StaticInit(&device)) + return false; + + + uint32_t refSpadCount; + uint8_t isApertureSpads; + + if (!VL53L0X_PerformRefSpadManagement(&device, &refSpadCount, &isApertureSpads)) + return false; + + + // TODO expose as API to be run before changing desk positions + uint8_t vhvSettings; + uint8_t phaseCal; + + if (!VL53L0X_PerformRefCalibration(&device, &vhvSettings, &phaseCal)) + return false; + + if (!VL53L0X_SetDeviceMode(&device, VL53L0X_DEVICEMODE_SINGLE_RANGING)) + return false; + + if (!VL53L0X_SetLimitCheckEnable(&device, VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, 1)) + return false; + + if (!VL53L0X_SetLimitCheckEnable(&device, VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, 1)) + return false; + + if (!VL53L0X_SetLimitCheckEnable(&device, VL53L0X_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 1)) + return false; + + if (!VL53L0X_SetLimitCheckValue(&device, VL53L0X_CHECKENABLE_RANGE_IGNORE_THRESHOLD, (FixPoint1616_t)( 1.5 * 0.023 * 65536))) + return false; + + return true; +} + + +bool VL53L0X::getSingleRangingMeasurement(VL53L0X_RangingMeasurementData_t *data) +{ + return VL53L0X_PerformSingleRangingMeasurement(&device, data); +} \ No newline at end of file diff --git a/src/lib/vl53l0x.h b/src/lib/vl53l0x.h new file mode 100644 index 0000000..4fa7a1d --- /dev/null +++ b/src/lib/vl53l0x.h @@ -0,0 +1,37 @@ +/* + + The Adafruit VL53L0X library is included because it's an easy way to get + access to the VL53L0X API headers. I will not be using the Adafruit library + however because it lacks a way to set the timing budget, which I found to be + very much required for accurate results. + +*/ + +#ifndef __vl53l0x +#define __vl53l0x + +#include "Arduino.h" +#include "Wire.h" +#include "vl53l0x_api.h" + + +// TODO if begin fails, return a struct with the step and API status code + +#define VL53L0X_BEGIN_SUCCESS 0 +#define VL53L0X_BEGIN_SUCCESS 0 + + +class VL53L0X +{ + public: + bool init(uint8_t address); + + // TODO set timing budget + + bool getSingleRangingMeasurement(VL53L0X_RangingMeasurementData_t* data); + + private: + VL53L0X_Dev_t device; +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b8c1e6e..6afbc7b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,47 @@ -int main() +#include +#include +#include +#include + +#include "./include/display.h" +#include "./include/heightsensor.h" +#include "./lib/menu.h" +#include "./lib/vl53l0x.h" + + +Adafruit_ST7789 display = Adafruit_ST7789(DISPLAY_PORT_CS, DISPLAY_PORT_DC, DISPLAY_PORT_RST); +VL53L0X heightSensor = VL53L0X(); + +Menu menu = Menu(&display); + + +void setup() { - return 0; + display.init(DISPLAY_WIDTH, DISPLAY_HEIGHT, SPI_MODE3); + display.setRotation(DISPLAY_ROTATION); + display.fillScreen(ST77XX_BLACK); + + menu.init(); + + // TODO draw "initializing" text + + Wire.begin(); + heightSensor.init(HEIGHTSENSOR_I2C_ADDRESS); + +/* + Wire.begin(); + if (!heightSensor.begin()) + { + // TODO draw "height sensor error" text + + while(1); + } + + display.fillScreen(ST77XX_BLACK); +*/ +} + +void loop() +{ + // TODO } \ No newline at end of file diff --git a/upload.ps1 b/upload.ps1 deleted file mode 100644 index 9b3c625..0000000 --- a/upload.ps1 +++ /dev/null @@ -1 +0,0 @@ -& platformio run --target upload \ No newline at end of file