From 3cdace542418bcca00a558a5cd4a1c01ed97759b Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 11:15:33 +0100 Subject: [PATCH 001/100] Added pin extender support. Made everything compile yet again. --- FluidNC/src/Extenders/Extenders.cpp | 58 ++++ FluidNC/src/Extenders/Extenders.h | 25 ++ FluidNC/src/Extenders/PCA9539.cpp | 271 ++++++++++++++++++ FluidNC/src/Extenders/PCA9539.h | 126 ++++++++ FluidNC/src/Extenders/PinExtender.h | 23 ++ FluidNC/src/Extenders/PinExtenderDriver.cpp | 12 + FluidNC/src/Extenders/PinExtenderDriver.h | 33 +++ FluidNC/src/Machine/I2CBus.cpp | 54 ++++ FluidNC/src/Machine/I2CBus.h | 33 +++ FluidNC/src/Machine/MachineConfig.cpp | 2 + FluidNC/src/Machine/MachineConfig.h | 4 + FluidNC/src/Main.cpp | 8 +- FluidNC/src/MotionControl.cpp | 4 +- FluidNC/src/Pins/ExtPinDetail.cpp | 93 ++++++ FluidNC/src/Pins/ExtPinDetail.h | 43 +++ FluidNC/test/Pins/GPIO.cpp | 2 +- UnitTests.vcxproj | 19 +- UnitTests.vcxproj.filters | 60 +++- X86TestSupport/TestSupport/Wire.cpp | 4 + X86TestSupport/TestSupport/Wire.h | 56 ++++ X86TestSupport/TestSupport/driver/uart.h | 2 + .../TestSupport/freertos/FreeRTOS.h | 2 + 22 files changed, 916 insertions(+), 18 deletions(-) create mode 100644 FluidNC/src/Extenders/Extenders.cpp create mode 100644 FluidNC/src/Extenders/Extenders.h create mode 100644 FluidNC/src/Extenders/PCA9539.cpp create mode 100644 FluidNC/src/Extenders/PCA9539.h create mode 100644 FluidNC/src/Extenders/PinExtender.h create mode 100644 FluidNC/src/Extenders/PinExtenderDriver.cpp create mode 100644 FluidNC/src/Extenders/PinExtenderDriver.h create mode 100644 FluidNC/src/Machine/I2CBus.cpp create mode 100644 FluidNC/src/Machine/I2CBus.h create mode 100644 FluidNC/src/Pins/ExtPinDetail.cpp create mode 100644 FluidNC/src/Pins/ExtPinDetail.h create mode 100644 X86TestSupport/TestSupport/Wire.cpp create mode 100644 X86TestSupport/TestSupport/Wire.h diff --git a/FluidNC/src/Extenders/Extenders.cpp b/FluidNC/src/Extenders/Extenders.cpp new file mode 100644 index 000000000..d2b46821f --- /dev/null +++ b/FluidNC/src/Extenders/Extenders.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" + +namespace Extenders { + PinExtender::PinExtender() : _driver(nullptr) {} + + void PinExtender::validate() const { + if (_driver) { + _driver->validate(); + } + } + void PinExtender::group(Configuration::HandlerBase& handler) { PinExtenderFactory::factory(handler, _driver); } + void PinExtender::init() { + if (_driver) { + _driver->init(); + } + } + + PinExtender::~PinExtender() { delete _driver; } + + Extenders::Extenders() { + for (int i = 0; i < 16; ++i) { + _pinDrivers[i] = nullptr; + } + } + + void Extenders::validate() const {} + + void Extenders::group(Configuration::HandlerBase& handler) { + for (int i = 0; i < 10; ++i) { + char tmp[11 + 3]; + tmp[0] = 0; + strcat(tmp, "pinextender"); + + for (size_t i = 0; i < 10; ++i) { + tmp[11] = char(i + '0'); + tmp[12] = '\0'; + handler.section(tmp, _pinDrivers[i]); + } + } + } + + void Extenders::init() { + for (int i = 0; i < 16; ++i) { + if (_pinDrivers[i] != nullptr) { + _pinDrivers[i]->init(); + } + } + } + + Extenders::~Extenders() { + for (int i = 0; i < 16; ++i) { + delete _pinDrivers[i]; + } + } +} diff --git a/FluidNC/src/Extenders/Extenders.h b/FluidNC/src/Extenders/Extenders.h new file mode 100644 index 000000000..3d83af476 --- /dev/null +++ b/FluidNC/src/Extenders/Extenders.h @@ -0,0 +1,25 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "../Configuration/GenericFactory.h" +#include "PinExtender.h" + +namespace Extenders { + class Extenders : public Configuration::Configurable { + public: + Extenders(); + + PinExtender* _pinDrivers[16]; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + void init(); + + ~Extenders(); + }; + + using PinExtenderFactory = Configuration::GenericFactory; +} diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp new file mode 100644 index 000000000..d7f40f2a1 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" +#include "PCA9539.h" +#include "../Logging.h" + +#include +#include + +namespace Extenders { + void PCA9539::claim(pinnum_t index) { + Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); + + _claimed |= mask; + } + + void PCA9539::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed &= ~mask; + } + + uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + auto err = bus->write(address, ®, 1); + + if (err) { + // log_info("Error writing to i2c bus. Code: " << err); + return 0; + } + + uint8_t inputData; + if (bus->read(address, &inputData, 1) != 1) { + // log_info("Error reading from i2c bus."); + } + + return inputData; + } + + void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + auto err = bus->write(address, data, 2); + + if (err) { + log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); + } + } + + void PCA9539::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); + } + + void PCA9539::group(Configuration::HandlerBase& handler) { + handler.item("interrupt0", _isrData[0]._pin); + handler.item("interrupt1", _isrData[1]._pin); + handler.item("interrupt2", _isrData[2]._pin); + handler.item("interrupt3", _isrData[3]._pin); + } + + void PCA9539::isrTaskLoop(void* arg) { + auto inst = static_cast(arg); + while (true) { + void* ptr; + if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { + ISRData* valuePtr = static_cast(ptr); + // log_info("PCA state change ISR"); + valuePtr->updateValueFromDevice(); + } + } + } + + void PCA9539::init() { + this->_i2cBus = config->_i2c; + + _isrQueue = xQueueCreate(16, sizeof(void*)); + xTaskCreatePinnedToCore(isrTaskLoop, // task + "isr_handler", // name for task + configMINIMAL_STACK_SIZE + 256, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + data._address = uint8_t(0x74 + i); + data._container = this; + data._valueBase = reinterpret_cast(&_value) + i; + + // Update the value first by reading it: + data.updateValueFromDevice(); + + if (!data._pin.undefined()) { + data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + + // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. + data._pin.attachInterrupt(updatePCAState, FALLING, &data); + } else { + // Reset valueBase so we know it's not bound to an ISR: + data._valueBase = nullptr; + } + } + } + + void PCA9539::ISRData::updateValueFromDevice() { + const uint8_t InputReg = 0; + auto i2cBus = _container->_i2cBus; + + auto r1 = I2CGetValue(i2cBus, _address, InputReg); + auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); + uint16_t oldValue = *_valueBase; + uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); + *_valueBase = value; + + if (_hasISR) { + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1) << i; + + if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { + // log_info("State change pin " << i); + switch (_isrMode[i]) { + case RISING: + if ((value & mask) == mask) { + _isrCallback[i](_isrArgument); + } + break; + case FALLING: + if ((value & mask) == 0) { + _isrCallback[i](_isrArgument); + } + break; + case CHANGE: + _isrCallback[i](_isrArgument); + break; + } + } + } + } + } + + void PCA9539::updatePCAState(void* ptr) { + ISRData* valuePtr = static_cast(ptr); + + BaseType_t xHigherPriorityTaskWoken = false; + xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); + } + + void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { + bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); + bool output = attr.has(Pins::PinAttributes::Output); + + uint64_t mask = uint64_t(1) << index; + _invert = (_invert & ~mask) | (activeLow ? mask : 0); + _configuration = (_configuration & ~mask) | (output ? 0 : mask); + + const uint8_t deviceId = index / 16; + + const uint8_t ConfigReg = 6; + uint8_t address = 0x74 + deviceId; + + uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); + uint8_t reg = ConfigReg + ((index / 8) & 1); + + // log_info("Setup reg " << int(reg) << " with value " << int(value)); + + I2CSetValue(_i2cBus, address, reg, value); + } + + void PCA9539::writePin(pinnum_t index, bool high) { + uint64_t mask = uint64_t(1) << index; + uint64_t oldVal = _value; + uint64_t newVal = high ? mask : uint64_t(0); + _value = (_value & ~mask) | newVal; + + _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); + } + + bool PCA9539::readPin(pinnum_t index) { + uint8_t reg = uint8_t(index / 8); + uint8_t deviceId = reg / 2; + + // If it's handled by the ISR, we don't need to read anything from the device. + // Otherwise, we do. Check: + if (_isrData[deviceId]._valueBase == nullptr) { + const uint8_t InputReg = 0; + uint8_t address = 0x74 + deviceId; + + auto readReg = InputReg + (reg & 1); + auto value = I2CGetValue(_i2cBus, address, readReg); + uint64_t newValue = uint64_t(value) << (int(reg) * 8); + uint64_t mask = uint64_t(0xff) << (int(reg) * 8); + + _value = ((newValue ^ _invert) & mask) | (_value & ~mask); + + // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } + // else { + // log_info("No read, value is " << int(_value)); + // } + + return (_value & (1ull << index)) != 0; + } + + void PCA9539::flushWrites() { + uint64_t write = _value ^ _invert; + for (int i = 0; i < 8; ++i) { + if ((_dirtyRegisters & (1 << i)) != 0) { + const uint8_t OutputReg = 2; + uint8_t address = 0x74 + (i / 2); + + uint8_t val = uint8_t(write >> (8 * i)); + uint8_t reg = OutputReg + (i & 1); + I2CSetValue(_i2cBus, address, reg, val); + } + } + + _dirtyRegisters = 0; + } + + // ISR's: + void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + int device = index / 16; + int pinNumber = index % 16; + + Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); + + _isrData[device]._isrCallback[pinNumber] = callback; + _isrData[device]._isrArgument[pinNumber] = arg; + _isrData[device]._isrMode[pinNumber] = mode; + _isrData[device]._hasISR = true; + } + + void PCA9539::detachInterrupt(pinnum_t index) { + int device = index / 16; + int pinNumber = index % 16; + + _isrData[device]._isrCallback[pinNumber] = nullptr; + _isrData[device]._isrArgument[pinNumber] = nullptr; + _isrData[device]._isrMode[pinNumber] = 0; + + bool hasISR = false; + for (int i = 0; i < 16; ++i) { + hasISR |= (_isrData[device]._isrArgument[i] != nullptr); + } + _isrData[device]._hasISR = hasISR; + } + + const char* PCA9539::name() const { return "pca9539"; } + + PCA9539 ::~PCA9539() { + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + if (!data._pin.undefined()) { + data._pin.detachInterrupt(); + } + } + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("pca9539"); + } +} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h new file mode 100644 index 000000000..d90068370 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.h @@ -0,0 +1,126 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + class PCA9539 : public PinExtenderDriver { + friend class Pins::PCA9539PinDetail; + + // Address can be set for up to 4 devices. Each device supports 16 pins. + + static const int numberPins = 16 * 4; + uint64_t _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Registers: + // 4x16 = 64 bits. Fits perfectly into an uint64. + uint64_t _configuration = 0; + uint64_t _invert = 0; + volatile uint64_t _value = 0; + + // 4 devices, 2 registers per device. 8 bits is enough: + uint8_t _dirtyRegisters = 0; + + QueueHandle_t _isrQueue = nullptr; + TaskHandle_t _isrHandler = nullptr; + + static void isrTaskLoop(void* arg); + + struct ISRData { + ISRData() = default; + + Pin _pin; + PCA9539* _container = nullptr; + volatile uint16_t* _valueBase = nullptr; + uint8_t _address = 0; + + typedef void (*ISRCallback)(void*); + + bool _hasISR = false; + ISRCallback _isrCallback[16] = { 0 }; + void* _isrArgument[16] = { 0 }; + int _isrMode[16] = { 0 }; + + void IRAM_ATTR updateValueFromDevice(); + }; + + ISRData _isrData[4]; + static void IRAM_ATTR updatePCAState(void* ptr); + + public: + PCA9539() = default; + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~PCA9539(); + }; +} diff --git a/FluidNC/src/Extenders/PinExtender.h b/FluidNC/src/Extenders/PinExtender.h new file mode 100644 index 000000000..2c60327c1 --- /dev/null +++ b/FluidNC/src/Extenders/PinExtender.h @@ -0,0 +1,23 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "PinExtenderDriver.h" + +namespace Extenders { + class PinExtender : public Configuration::Configurable { + public: + // Other configurations? + PinExtenderDriver* _driver; + + PinExtender(); + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + void init(); + + ~PinExtender(); + }; +} diff --git a/FluidNC/src/Extenders/PinExtenderDriver.cpp b/FluidNC/src/Extenders/PinExtenderDriver.cpp new file mode 100644 index 000000000..efc602acc --- /dev/null +++ b/FluidNC/src/Extenders/PinExtenderDriver.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "PinExtenderDriver.h" + +namespace Extenders { + + void PinExtenderDriver::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + Assert(false, "Interrupts are not supported by pin extender for pin %d", index); + } + void PinExtenderDriver::detachInterrupt(pinnum_t index) { Assert(false, "Interrupts are not supported by pin extender"); } +} diff --git a/FluidNC/src/Extenders/PinExtenderDriver.h b/FluidNC/src/Extenders/PinExtenderDriver.h new file mode 100644 index 000000000..dfc4e79ad --- /dev/null +++ b/FluidNC/src/Extenders/PinExtenderDriver.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "../Pins/PinAttributes.h" + +#include "../Platform.h" + +namespace Extenders { + class PinExtenderDriver : public Configuration::Configurable { + public: + virtual void init() = 0; + + virtual void claim(pinnum_t index) = 0; + virtual void free(pinnum_t index) = 0; + + virtual void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) = 0; + virtual void IRAM_ATTR writePin(pinnum_t index, bool high) = 0; + virtual bool IRAM_ATTR readPin(pinnum_t index) = 0; + virtual void IRAM_ATTR flushWrites() = 0; + + virtual void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode); + virtual void detachInterrupt(pinnum_t index); + + // Name is required for the configuration factory to work. + virtual const char* name() const = 0; + + // Virtual base classes require a virtual destructor. + virtual ~PinExtenderDriver() {} + }; +} diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp new file mode 100644 index 000000000..efc6dded3 --- /dev/null +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "I2CBus.h" + +namespace Machine { + void I2CBus::validate() const { + if (_sda.defined() || _scl.defined()) { + Assert(_sda.defined(), "I2C SDA pin should be configured once"); + Assert(_scl.defined(), "I2C SCL pin should be configured once"); + Assert(_busNumber == 0 || _busNumber == 1, "The ESP32 only has 2 I2C buses. Number %d is invalid", _busNumber); + } + } + + void I2CBus::group(Configuration::HandlerBase& handler) { + handler.item("sda", _sda); + handler.item("scl", _scl); + handler.item("busNumber", _busNumber); + handler.item("frequency", _frequency); + } + + void I2CBus::init() { + log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); + + auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + + if (_busNumber == 0) { + i2c = &Wire; + } else { + i2c = &Wire1; + } + i2c->begin(sdaPin, sclPin /*, _frequency */); + } + + int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { + i2c->beginTransmission(address); + for (size_t i = 0; i < count; ++i) { + i2c->write(data[i]); + } + return i2c->endTransmission(); // i2c_err_t ?? + } + + int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { + for (size_t i = 0; i < count; ++i) { + if (i2c->requestFrom((int)address, 1) != 1) { + return i; + } + data[i] = i2c->read(); + } + return count; + } + +} diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h new file mode 100644 index 000000000..a55673ae5 --- /dev/null +++ b/FluidNC/src/Machine/I2CBus.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" + +#include +#include + +namespace Machine { + class I2CBus : public Configuration::Configurable { + protected: + TwoWire* i2c; + + public: + I2CBus() = default; + + int _busNumber = 0; + Pin _sda; + Pin _scl; + uint32_t _frequency = 0; + + void init(); + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! + int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); + + ~I2CBus() = default; + }; +} diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index bb0a3be67..377b64929 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -40,12 +40,14 @@ namespace Machine { handler.section("axes", _axes); handler.section("kinematics", _kinematics); handler.section("i2so", _i2so); + handler.section("i2c", _i2c); handler.section("spi", _spi); handler.section("sdcard", _sdCard); handler.section("control", _control); handler.section("coolant", _coolant); handler.section("probe", _probe); handler.section("macros", _macros); + handler.section("extenders", _extenders); handler.section("start", _start); handler.section("user_outputs", _userOutputs); diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index d87181eb1..be8eb7f9c 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -11,6 +11,7 @@ #include "../CoolantControl.h" #include "../Kinematics/Kinematics.h" #include "../WebUI/BTConfig.h" +#include "../Extenders/Extenders.h" #include "../Control.h" #include "../Probe.h" #include "../SDCard.h" @@ -21,6 +22,7 @@ #include "../Config.h" #include "Axes.h" #include "SPIBus.h" +#include "I2CBus.h" #include "I2SOBus.h" #include "UserOutputs.h" #include "Macros.h" @@ -58,6 +60,7 @@ namespace Machine { Axes* _axes = nullptr; Kinematics* _kinematics = nullptr; SPIBus* _spi = nullptr; + I2CBus* _i2c = nullptr; I2SOBus* _i2so = nullptr; Stepping* _stepping = nullptr; CoolantControl* _coolant = nullptr; @@ -68,6 +71,7 @@ namespace Machine { Macros* _macros = nullptr; Start* _start = nullptr; Spindles::SpindleList _spindles; + Extenders::Extenders* _extenders = nullptr; float _arcTolerance = 0.002f; float _junctionDeviation = 0.01f; diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 94db30bbb..b0fe7e696 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -69,17 +69,19 @@ void setup() { config->_sdCard->init(); } } + if (config->_i2c) { + config->_i2c->init(); + } + + // TODO FIXME: Initialize extenders *here* config->_stepping->init(); // Configure stepper interrupt timers plan_init(); config->_userOutputs->init(); - config->_axes->init(); - config->_control->init(); - config->_kinematics->init(); memset(motor_steps, 0, sizeof(motor_steps)); // Clear machine position. diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index ee11f267b..7c2caa7f0 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -132,7 +132,7 @@ void mc_arc(float* target, auto n_axis = config->_axes->_numberAxis; - float previous_position[n_axis] = { 0.0 }; + float previous_position[MAX_N_AXIS] = { 0.0f }; for (size_t i = 0; i < n_axis; i++) { previous_position[i] = position[i]; } @@ -164,7 +164,7 @@ void mc_arc(float* target, pl_data->motion.inverseTime = 0; // Force as feed absolute mode over arc segments. } float theta_per_segment = angular_travel / segments; - float linear_per_segment[n_axis]; + float linear_per_segment[MAX_N_AXIS]; linear_per_segment[axis_linear] = (target[axis_linear] - position[axis_linear]) / segments; for (size_t i = A_AXIS; i < n_axis; i++) { linear_per_segment[i] = (target[i] - position[i]) / segments; diff --git a/FluidNC/src/Pins/ExtPinDetail.cpp b/FluidNC/src/Pins/ExtPinDetail.cpp new file mode 100644 index 000000000..a5809f1b0 --- /dev/null +++ b/FluidNC/src/Pins/ExtPinDetail.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "ExtPinDetail.h" + +namespace Pins { + ExtPinDetail::ExtPinDetail(int device, pinnum_t index, const PinOptionsParser& options) : + PinDetail(index), _device(device), _capabilities(PinCapabilities::Output | PinCapabilities::Input | PinCapabilities::ISR), + _attributes(Pins::PinAttributes::Undefined) { + // User defined pin capabilities + for (auto opt : options) { + if (opt.is("low")) { + _attributes = _attributes | PinAttributes::ActiveLow; + } else if (opt.is("high")) { + // Default: Active HIGH. + } else { + Assert(false, "Unsupported I2SO option '%s'", opt()); + } + } + } + + PinCapabilities ExtPinDetail::capabilities() const { return PinCapabilities::Input | PinCapabilities::Output | PinCapabilities::ISR; } + + // I/O: + void ExtPinDetail::write(int high) { + Assert(_owner != nullptr, "Cannot write to uninitialized pin"); + _owner->writePin(_index, high); + } + + void ExtPinDetail::synchronousWrite(int high) { + Assert(_owner != nullptr, "Cannot write to uninitialized pin"); + _owner->writePin(_index, high); + _owner->flushWrites(); + } + + int ExtPinDetail::read() { + Assert(_owner != nullptr, "Cannot read from uninitialized pin"); + return _owner->readPin(_index); + } + + void ExtPinDetail::setAttr(PinAttributes value) { + // We setup the driver in setAttr. Before this time, the owner might not be valid. + + // Check the attributes first: + Assert(value.has(PinAttributes::Input) || value.has(PinAttributes::Output), "PCA9539 pins can be used as either input or output."); + Assert(value.has(PinAttributes::Input) != value.has(PinAttributes::Output), "PCA9539 pins can be used as either input or output."); + Assert(value.validateWith(this->_capabilities), "Requested attributes do not match the PCA9539 pin capabilities."); + Assert(!_attributes.conflictsWith(value), "Attributes on this pin have been set before, and there's a conflict."); + + _attributes = value; + + bool activeLow = _attributes.has(PinAttributes::ActiveLow); + + if (_owner == nullptr) { + auto ext = config->_extenders; + if (ext != nullptr && ext->_pinDrivers[_device] != nullptr && ext->_pinDrivers[_device]->_driver != nullptr) { + _owner = ext->_pinDrivers[_device]->_driver; + } else { + Assert(false, "Cannot find pin extender definition in configuration for pin pinext%d.%d", _device, _index); + } + + _owner->claim(_index); + } + + _owner->setupPin(_index, _attributes); + _owner->writePin(_index, value.has(PinAttributes::InitialOn)); + } + + PinAttributes ExtPinDetail::getAttr() const { return _attributes; } + + void ExtPinDetail::attachInterrupt(void (*callback)(void*), void* arg, int mode) { + Assert(_owner != nullptr, "Cannot attach ISR on uninitialized pin"); + _owner->attachInterrupt(_index, callback, arg, mode); + } + void ExtPinDetail::detachInterrupt() { + Assert(_owner != nullptr, "Cannot detach ISR on uninitialized pin"); + _owner->detachInterrupt(_index); + } + + String ExtPinDetail::toString() { + auto s = String("pinext") + int(_device) + String(".") + int(_index); + if (_attributes.has(PinAttributes::ActiveLow)) { + s += ":low"; + } + return s; + } + + ExtPinDetail::~ExtPinDetail() { + if (_owner) { + _owner->free(_index); + } + } +} diff --git a/FluidNC/src/Pins/ExtPinDetail.h b/FluidNC/src/Pins/ExtPinDetail.h new file mode 100644 index 000000000..df1f9320f --- /dev/null +++ b/FluidNC/src/Pins/ExtPinDetail.h @@ -0,0 +1,43 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinDetail.h" +#include "../Extenders/PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" + +#include + +namespace Pins { + class ExtPinDetail : public PinDetail { + Extenders::PinExtenderDriver* _owner = nullptr; + int _device; + + PinCapabilities _capabilities; + PinAttributes _attributes; + + public: + ExtPinDetail(int device, pinnum_t index, const PinOptionsParser& options); + + PinCapabilities capabilities() const override; + + // I/O: + void write(int high) override; + void synchronousWrite(int high) override; + int read() override; + + // ISR's: + void attachInterrupt(void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt() override; + + void setAttr(PinAttributes value) override; + PinAttributes getAttr() const override; + + String toString() override; + + ~ExtPinDetail() override; + }; +} diff --git a/FluidNC/test/Pins/GPIO.cpp b/FluidNC/test/Pins/GPIO.cpp index c17acdde3..39ec3d49d 100644 --- a/FluidNC/test/Pins/GPIO.cpp +++ b/FluidNC/test/Pins/GPIO.cpp @@ -301,7 +301,7 @@ namespace Pins { hitCount = 0; int expected = 0; - gpio16.attachInterrupt(this, mode); + // gpio16.attachInterrupt(this, mode); // Two ways to set I/O: // 1. using on/off diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 1f9d64333..93d739c8d 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -33,8 +33,15 @@ + + + + + + + @@ -97,7 +104,6 @@ - @@ -206,10 +212,17 @@ + + + + + + + @@ -223,6 +236,7 @@ + @@ -240,7 +254,6 @@ - @@ -305,7 +318,6 @@ - @@ -347,6 +359,7 @@ + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 34ac5140a..d8f5ac631 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -46,6 +46,9 @@ {dc4ba3bc-4342-4a67-aea2-f68877f1ee69} + + {2b9b9202-4bb3-450e-a90b-99f042de0af2} + @@ -225,9 +228,6 @@ src\Configuration - - src - src\Motors @@ -573,6 +573,30 @@ X86TestSupport + + X86TestSupport + + + src + + + src\Machine + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + src\Pins + @@ -665,9 +689,6 @@ src\Configuration - - src - src\StackTrace @@ -815,9 +836,6 @@ src\Spindles - - src - src\Machine @@ -989,6 +1007,30 @@ X86TestSupport + + src + + + src + + + src\Machine + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + X86TestSupport + + + src\Pins + diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp new file mode 100644 index 000000000..046c93eda --- /dev/null +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -0,0 +1,4 @@ +#include "Wire.h" + +TwoWire Wire(0); +TwoWire Wire1(1); diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h new file mode 100644 index 000000000..aa9f01dcf --- /dev/null +++ b/X86TestSupport/TestSupport/Wire.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include "Stream.h" + +class TwoWire : public Stream { +public: + TwoWire(uint8_t bus_num) {} + ~TwoWire() {} + + //call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl) {} + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } + bool end() { return true; } + + void setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms + uint16_t getTimeOut() { return 0; } + + bool setClock(uint32_t) {} + uint32_t getClock() { return 0; } + + void beginTransmission(uint16_t address) {} + void beginTransmission(uint8_t address) {} + void beginTransmission(int address) {} + + uint8_t endTransmission(bool sendStop) { return 0; } + uint8_t endTransmission(void) { return 0; } + + size_t requestFrom(uint16_t address, size_t size, bool sendStop) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { return 0; } + size_t requestFrom(uint8_t address, size_t len, bool stopBit) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t size) { return 0; } + uint8_t requestFrom(int address, int size, int sendStop) { return 0; } + uint8_t requestFrom(int address, int size) { return 0; } + + size_t write(uint8_t) { return 0; } + size_t write(const uint8_t*, size_t) { return 0; } + int available(void) { return 0; } + int read(void) { return 0; } + int peek(void) { return 0; } + void flush(void) {} + + inline size_t write(const char* s) { return write((uint8_t*)s, strlen(s)); } + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } +}; + +extern TwoWire Wire; +extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/driver/uart.h b/X86TestSupport/TestSupport/driver/uart.h index 9b7192685..8efa6add0 100644 --- a/X86TestSupport/TestSupport/driver/uart.h +++ b/X86TestSupport/TestSupport/driver/uart.h @@ -77,6 +77,8 @@ typedef struct { bool use_ref_tick; /*!< Set to true if UART should be clocked from REF_TICK */ } uart_config_t; +const int UART_FIFO_LEN = 128; + esp_err_t uart_flush(uart_port_t uart_num); esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t* uart_config); esp_err_t uart_driver_install( diff --git a/X86TestSupport/TestSupport/freertos/FreeRTOS.h b/X86TestSupport/TestSupport/freertos/FreeRTOS.h index 6b20e1612..11df765a5 100644 --- a/X86TestSupport/TestSupport/freertos/FreeRTOS.h +++ b/X86TestSupport/TestSupport/freertos/FreeRTOS.h @@ -29,3 +29,5 @@ inline void vTaskEnterCritical(portMUX_TYPE* mux) { inline int32_t xPortGetFreeHeapSize() { return 1024 * 1024 * 4; } + +#define configMINIMAL_STACK_SIZE 768 From fe8cde7b2e09a6a66fa52a980cf038bf82eb60cb Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 17:03:09 +0100 Subject: [PATCH 002/100] Added some test code to first check if the pin extender works. Seems to be fine. --- FluidNC/src/Extenders/PCA9539.cpp | 23 ++++---- FluidNC/src/Machine/I2CBus.cpp | 12 +++- FluidNC/src/Machine/I2CBus.h | 19 ++++++- FluidNC/src/Main.cpp | 94 ++++++++++++++++++++++++++++++- FluidNC/src/Pin.cpp | 11 ++++ 5 files changed, 143 insertions(+), 16 deletions(-) diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp index d7f40f2a1..eea3cb056 100644 --- a/FluidNC/src/Extenders/PCA9539.cpp +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -24,22 +24,26 @@ namespace Extenders { } uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); + auto err = bus->write(address, ®, 1); if (err) { - // log_info("Error writing to i2c bus. Code: " << err); + log_info("Error writing to i2c bus. Code: " << err); return 0; } uint8_t inputData; if (bus->read(address, &inputData, 1) != 1) { - // log_info("Error reading from i2c bus."); + log_info("Error reading from i2c bus."); } return inputData; } void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); + uint8_t data[2]; data[0] = reg; data[1] = uint8_t(value); @@ -68,7 +72,7 @@ namespace Extenders { void* ptr; if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { ISRData* valuePtr = static_cast(ptr); - // log_info("PCA state change ISR"); + log_info("PCA state change ISR"); valuePtr->updateValueFromDevice(); } } @@ -80,7 +84,7 @@ namespace Extenders { _isrQueue = xQueueCreate(16, sizeof(void*)); xTaskCreatePinnedToCore(isrTaskLoop, // task "isr_handler", // name for task - configMINIMAL_STACK_SIZE + 256, // size of task stack + configMINIMAL_STACK_SIZE + 512, // size of task stack this, // parameters 1, // priority &_isrHandler, @@ -124,7 +128,7 @@ namespace Extenders { uint16_t mask = uint16_t(1) << i; if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { - // log_info("State change pin " << i); + log_info("State change pin " << i); switch (_isrMode[i]) { case RISING: if ((value & mask) == mask) { @@ -168,7 +172,7 @@ namespace Extenders { uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); uint8_t reg = ConfigReg + ((index / 8) & 1); - // log_info("Setup reg " << int(reg) << " with value " << int(value)); + log_info("Setup reg " << int(reg) << " with value " << int(value)); I2CSetValue(_i2cBus, address, reg, value); } @@ -199,11 +203,10 @@ namespace Extenders { _value = ((newValue ^ _invert) & mask) | (_value & ~mask); - // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } else { + log_info("No read, value is " << int(_value)); } - // else { - // log_info("No read, value is " << int(_value)); - // } return (_value & (1ull << index)) != 0; } diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index efc6dded3..7158661de 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -3,6 +3,8 @@ #include "I2CBus.h" +#include + namespace Machine { void I2CBus::validate() const { if (_sda.defined() || _scl.defined()) { @@ -20,8 +22,6 @@ namespace Machine { } void I2CBus::init() { - log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); - auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); @@ -31,9 +31,14 @@ namespace Machine { i2c = &Wire1; } i2c->begin(sdaPin, sclPin /*, _frequency */); + + log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); } int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { + // log_info("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); + i2c->beginTransmission(address); for (size_t i = 0; i < count; ++i) { i2c->write(data[i]); @@ -42,6 +47,9 @@ namespace Machine { } int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { + // log_info("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); + for (size_t i = 0; i < count; ++i) { if (i2c->requestFrom((int)address, 1) != 1) { return i; diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index a55673ae5..febbf51fc 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -5,13 +5,14 @@ #include "../Configuration/Configurable.h" -#include #include +class TwoWire; + namespace Machine { class I2CBus : public Configuration::Configurable { protected: - TwoWire* i2c; + TwoWire* i2c = nullptr; public: I2CBus() = default; @@ -25,6 +26,20 @@ namespace Machine { void validate() const override; void group(Configuration::HandlerBase& handler) override; + /* + typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN + } i2c_err_t; + */ + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index b0fe7e696..06e92cd44 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -28,6 +28,51 @@ extern void make_user_commands(); +// FOR TESTING: + +# include +# include + +extern "C" void __pinMode(pinnum_t pin, uint8_t mode); + +uint8_t I2CGetValue(uint8_t address, uint8_t reg) { + Wire.beginTransmission(address); + Wire.write(reg); + auto err = Wire.endTransmission(); // i2c_err_t + + if (Wire.requestFrom((int)address, 1) != 1) { + Uart0.println("Error reading from i2c bus."); + return 0; + } + uint8_t result = Wire.read(); + return result; +} + +void I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + + Wire.beginTransmission(address); + for (size_t i = 0; i < 2; ++i) { + Wire.write(data[i]); + } + auto err = Wire.endTransmission(); // i2c_err_t ?? + + if (err) { + Uart0.println("Error writing to i2c bus; PCA9539 failed. Code: "); + Uart0.println(int(err)); + } +} + +volatile bool fired = false; + +void isrHandler() { + fired = true; +} + +// --- Until here. + void setup() { try { uartInit(); // Setup serial port @@ -38,6 +83,49 @@ void setup() { WebUI::WiFiConfig::reset(); + /* TEST STUFF! */ + + /* + // THIS WORKS: + { + Uart0.println("Basic test of pin extender."); + // Wire.begin(sda , scl, frequency); + Wire.begin(13, 14, 100000); + + Uart0.println("Setup pins:"); + + // 1. Setup pins: + I2CSetValue(0x74, 6, 0xFF); // All input pins + I2CSetValue(0x74, 7, 0xFF); // All input pins + + __pinMode(36, INPUT); + attachInterrupt(36, isrHandler, CHANGE); + + // 2. Read input register: + Uart0.println("Main loop:"); + while (true) { + auto r1 = I2CGetValue(0x74, 0); + auto r2 = I2CGetValue(0x74, 1); + uint16_t v = (uint16_t(r1) << 8) | uint16_t(r2); + Uart0.print("Status: "); + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1 << i); + Uart0.print((v & mask) ? '1' : '0'); + } + + if (fired) { + Uart0.print(" ** ISR"); + fired = false; + } + + Uart0.println(); + + delay(1000); + } + } + */ + /* END TEST STUFF */ + display_init(); // Load settings from non-volatile storage @@ -73,7 +161,10 @@ void setup() { config->_i2c->init(); } - // TODO FIXME: Initialize extenders *here* + // We have to initialize the extenders first, before pins are used + if (config->_extenders) { + config->_extenders->init(); + } config->_stepping->init(); // Configure stepper interrupt timers @@ -119,7 +210,6 @@ void setup() { config->_coolant->init(); config->_probe->init(); } - } catch (const AssertionFailed& ex) { // This means something is terribly broken: log_error("Critical error in main_init: " << ex.what()); diff --git a/FluidNC/src/Pin.cpp b/FluidNC/src/Pin.cpp index 0f825ff76..0cbee91ce 100644 --- a/FluidNC/src/Pin.cpp +++ b/FluidNC/src/Pin.cpp @@ -10,6 +10,7 @@ #include "Pins/VoidPinDetail.h" #include "Pins/I2SOPinDetail.h" #include "Pins/ErrorPinDetail.h" +#include "Pins/ExtPinDetail.h" #include // snprintf() Pins::PinDetail* Pin::undefinedPin = new Pins::VoidPinDetail(); @@ -94,6 +95,16 @@ const char* Pin::parse(StringRange tmp, Pins::PinDetail*& pinImplementation) { pinImplementation = new Pins::VoidPinDetail(); } + if (prefix.startsWith("pinext")) { + if (prefix.length() == 7 && prefix[6] >= '0' && prefix[6] <= '9') { + auto deviceId = prefix[6] - '0'; + pinImplementation = new Pins::ExtPinDetail(deviceId, pinnum_t(pinNumber), parser); + } else { + // For now this should be sufficient, if not we can easily change it to 100 extenders: + return "Incorrect pin extender specification. Expected 'pinext[0-9].[port number]'."; + } + } + if (pinImplementation == nullptr) { log_error("Unknown prefix:" << prefix); return "Unknown pin prefix"; From 98fece4ae853bd6f022b0ea7ba6585bcb45df5c0 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Wed, 2 Mar 2022 14:50:21 +0100 Subject: [PATCH 003/100] Rewrite of I2C extender code. --- FluidNC/src/Extenders/I2CExtender.cpp | 408 +++++++++++++++++++++ FluidNC/src/Extenders/I2CExtender.h | 176 +++++++++ FluidNC/src/Extenders/PCA9539.cpp | 274 -------------- FluidNC/src/Extenders/PCA9539.h | 126 ------- FluidNC/src/Machine/I2CBus.cpp | 41 ++- FluidNC/src/Machine/I2CBus.h | 20 +- UnitTests.vcxproj | 5 +- UnitTests.vcxproj.filters | 15 +- X86TestSupport/TestSupport/Wire.h | 1 + X86TestSupport/TestSupport/esp32-hal-i2c.h | 13 + 10 files changed, 648 insertions(+), 431 deletions(-) create mode 100644 FluidNC/src/Extenders/I2CExtender.cpp create mode 100644 FluidNC/src/Extenders/I2CExtender.h delete mode 100644 FluidNC/src/Extenders/PCA9539.cpp delete mode 100644 FluidNC/src/Extenders/PCA9539.h create mode 100644 X86TestSupport/TestSupport/esp32-hal-i2c.h diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp new file mode 100644 index 000000000..8a824a24f --- /dev/null +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -0,0 +1,408 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "I2CExtender.h" + +#include "Extenders.h" +#include "../Logging.h" +#include "../Assert.h" +#include "../Machine/I2CBus.h" + +#include +#include +#include + +namespace Extenders { + I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} + + uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + int err; + if ((err = bus->write(address, ®, 1)) != 0) { + log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + return 0; + } else { + uint8_t result = 0; + if (bus->read(address, &result, 1) != 1) { + log_warn("Cannot read from I2C bus: " + << "no response"); + } + return result; + } + } + void I2CExtender::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + + int err = bus->write(address, data, 2); + + if (err) { + log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + } + } + + void I2CExtender::isrTaskLoopDetail() { + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + int registersPerDevice = _ports / 8; + int claimedValues = 0; + uint8_t commonStatus = _operation; + + // Update everything the first operation + _status = 1; + if (_outputReg != 0xFF) { + _status |= 4; // writes + } + if (_inputReg != 0xFF) { + _status |= 8; // reads + } + + // Main loop for I2C handling: + while (true) { + uint8_t newStatus = 0; + + newStatus = _status.exchange(newStatus); + newStatus |= commonStatus; + + if (newStatus != 0) { + if ((newStatus & 2) != 0) { + break; + } + + // Update config: + if ((newStatus & 1) != 0) { + // First fence! + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + + // Configuration dirty. Update _configuration and _invert. + // + // First check how many u8's are claimed: + claimedValues = 0; + for (int i = 0; i < 8; ++i) { + if (_claimed.bytes[i] != 0) { + claimedValues = i; + } + } + // Configuration: + { + for (int i = 0; i < claimedValues; ++i) { + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + + uint8_t by = _configuration.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + + currentRegister++; + if (currentRegister == registersPerDevice + _operationReg) { + ++address; + } + } + } + // Invert: + if (_invertReg != 0xFF) { + uint8_t currentRegister = _invertReg; + uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { + uint8_t by = _invert.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + + currentRegister++; + if (currentRegister == registersPerDevice + _invertReg) { + ++address; + } + } + } + + // Configuration changed. Writes and reads must be updated. + if (_outputReg != 0xFF) { + newStatus |= 4; // writes + } + if (_inputReg != 0xFF) { + newStatus |= 8; // reads + } + + commonStatus = _operation; + } + + // Handle writes: + if ((newStatus & 4) != 0) { + uint8_t currentRegister = _outputReg; + uint8_t address = _address; + + bool handleInvertSoftware = (_invertReg == 0xFF); + + auto toWrite = _dirtyWrite.exchange(0); + for (int i = 0; i < claimedValues; ++i) { + if ((toWrite & (1 << i)) != 0) { + uint8_t by = handleInvertSoftware ? (_output.bytes[i] ^ _invert.bytes[i]) : _output.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + } + + currentRegister++; + if (currentRegister == registersPerDevice + _outputReg) { + ++address; + } + } + } + + // Handle reads: + if ((newStatus & 8) != 0) { + uint8_t currentRegister = _inputReg; + uint8_t address = _address; + + // If we don't have an ISR, we must update everything. Otherwise, we can cherry pick: + bool handleInvertSoftware = (_invertReg == 0xFF); + + for (int i = 0; i < claimedValues; ++i) { + auto oldByte = _input.bytes[i]; + auto newByte = I2CGetValue(this->_i2cBus, address, currentRegister); + if (handleInvertSoftware) { + newByte ^= _invert.bytes[i]; + } + + if (oldByte != newByte) { + // Handle ISR's: + _input.bytes[i] = newByte; + int offset = claimedValues * 8; + for (int j = 0; j < 8; ++j) { + auto isr = _isrData[offset + j]; + if (isr.defined()) { + auto mask = uint8_t(1 << j); + auto o = (oldByte & mask); + auto n = (newByte & mask); + if (o != n) { + isr.callback(isr.data); + } + } + } + } + + currentRegister++; + if (currentRegister == registersPerDevice + _invertReg) { + ++address; + } + } + } + } + + vTaskDelay(TaskDelayBetweenIterations); + } + } + + void I2CExtender::isrTaskLoop(void* arg) { static_cast(arg)->isrTaskLoopDetail(); } + + void I2CExtender::claim(pinnum_t index) { + Assert(index >= 0 && index < 64, "I2CExtender IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed.value & mask) == 0, "I2CExtender IO port %d is already used.", index); + + _claimed.value |= mask; + } + + void I2CExtender::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed.value &= ~mask; + } + + void I2CExtender::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "I2CExtender works through I2C, but I2C is not configured."); + + // We cannot validate _i2cBus, because that's initialized during `init`. + Assert(_device != int(I2CExtenderDevice::Unknown), "I2C device type is unknown. Cannot continue initializing extender."); + } + + void I2CExtender::group(Configuration::HandlerBase& handler) { + // device: pca9539 + // device_id: 0 + // interrupt: gpio.36 + handler.item("device", _device); + handler.item("device_id", _deviceId); + handler.item("interrupt", _interruptPin); + } + + void I2CExtender::interruptHandler(void* arg) { + auto ext = static_cast(arg); + ext->_status |= 8; + } + + void I2CExtender::init() { + Assert(_isrHandler == nullptr, "Init has already been called on I2C extender."); + + switch (I2CExtenderDevice(_device)) { + case I2CExtenderDevice::PCA9539: + // See data sheet page 7+: + _address = 0x74 + _deviceId; + _ports = 16; + _inputReg = 0; + _outputReg = 2; + _invertReg = 4; + _operationReg = 6; + break; + + default: + Assert(false, "Pin extender device is not supported!"); + break; + } + + xTaskCreatePinnedToCore(isrTaskLoop, // task + "i2cHandler", // name for task + configMINIMAL_STACK_SIZE + 512, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + if (_interruptPin.defined()) { + _interruptPin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + _interruptPin.attachInterrupt(interruptHandler, FALLING, this); + } + } + + void IRAM_ATTR I2CExtender::setupPin(pinnum_t index, Pins::PinAttributes attr) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + _usedIORegisters |= uint8_t(1 << (index / 8)); + + uint64_t mask = 1ull << index; + + if (attr.has(Pins::PinAttributes::Input)) { + _configuration.value |= mask; + + if (attr.has(Pins::PinAttributes::PullUp)) { + _output.value |= mask; + } else if (attr.has(Pins::PinAttributes::PullDown)) { + _output.value &= ~mask; + } + } else if (attr.has(Pins::PinAttributes::Output)) { + _configuration.value &= ~mask; + + if (attr.has(Pins::PinAttributes::InitialOn)) { + _output.value |= mask; + } + } + + if (attr.has(Pins::PinAttributes::ActiveLow)) { + _invert.value |= mask; + } + + // Ignore the ISR flag. ISR is fine. + + // Trigger an update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + _status |= 1; + } + + void IRAM_ATTR I2CExtender::writePin(pinnum_t index, bool high) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + uint64_t mask = 1ull << index; + if (high) { + _output.value |= mask; + } else { + _output.value &= ~mask; + } + + uint8_t dirtyMask = uint8_t(1 << (index / 8)); + _dirtyWriteBuffer |= dirtyMask; + + // Note that _status is *not* updated! flushWrites takes care of this! + } + + bool IRAM_ATTR I2CExtender::readPin(pinnum_t index) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + // There are two possibilities here: + // 1. We use an ISR, and that we can just use the information as-is as long as it's in sync. + // The ISR itself triggers the update. + // 2. We don't use an ISR and need to update from I2C before we can reliably use the value. + + if (!_interruptPin.defined()) { + _status |= 8; + } + while (_status != 0) { + vTaskDelay(1); + } + + // Use the value: + return ((_input.value >> index) & 1) == 1; + } + + void IRAM_ATTR I2CExtender::flushWrites() { + auto writeMask = _dirtyWriteBuffer.exchange(0); + + _dirtyWrite |= writeMask; + _status |= 4; + + while (_status != 0) { + vTaskDelay(1); + } + } + + void I2CExtender::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + Assert(mode == CHANGE, "Only mode CHANGE is allowed for pin extender ISR's."); + Assert(index < 64 && index >= 0, "Pin index out of range"); + + ISRData& data = _isrData[index]; + data.callback = callback; + data.data = arg; + + // Update continuous operation. + _operation &= ~8; + if (!_interruptPin.defined()) { + _operation |= 8 | 16; + } + + // Trigger task configuration update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // write fence first! + _status |= 1; + } + + void I2CExtender::detachInterrupt(pinnum_t index) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + ISRData& data = _isrData[index]; + data.callback = nullptr; + data.data = nullptr; + + // Check if we still need to ISR everything. Use a temporary to ensure thread safety: + auto newop = _operation; + newop &= ~8; + if (!_interruptPin.defined()) { + for (int i = 0; i < 64; ++i) { + if (_isrData[i].defined()) { + newop |= 8 | 16; + } + } + } + _operation = newop; + + // Trigger task configuration update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // write fence first! + _status |= 1; + } + + const char* I2CExtender::name() const { return "i2c_extender"; } + + I2CExtender::~I2CExtender() { + // The task might have allocated temporary data, so we cannot just destroy it: + _status |= 2; + + // Detach the interrupt pin: + if (_interruptPin.defined()) { + _interruptPin.detachInterrupt(); + } + + // Give enough time for the task to stop: + vTaskDelay(TaskDelayBetweenIterations * 10); + + // Should be safe now to stop. + _isrHandler = nullptr; + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("i2c_extender"); + } +} diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h new file mode 100644 index 000000000..b98ff6037 --- /dev/null +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -0,0 +1,176 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + enum class I2CExtenderDevice { + Unknown, + PCA9539, + }; + + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + // + // How this class works... + // + // A single device has a bunch of pins, normally 8 or 16. These devices also have an address. + // The maximum number of extender I/O pins that we currently support is currently 64. Note that + // the I2C frequency doesn't really matter if you have the ISR handled. We limit the maximum number + // of ports to 64 here, which basically means you *can* wire multiple devices on a single ISR line + // by making good use of the addresses. + // + // Keep in mind that low latency is only possible if you do things correctly with placement on the + // PCB, and with designating I/O ports for very specific purposes. If not, the firmware won't know + // what an interrupt means, and has to figure it out before it can take action. + // + // A typical configuration looks like: + // + // device: pca9539 + // device_id: 0 + // interrupt: gpio.36 + // + class I2CExtender : public PinExtenderDriver { + struct RegisterSet { + union { + uint64_t value = 0; + uint8_t bytes[8]; + }; + }; + + struct ISRData { + using ISRCallback = void (*)(void*); + ISRData() : callback(nullptr), data(nullptr) {} + + ISRCallback callback; + void* data; + + inline bool defined() const { return callback != nullptr; } + }; + + // Device info: + int _device = int(I2CExtenderDevice::Unknown); + int _deviceId = 0; + + static const int TaskDelayBetweenIterations = 10; + + // Operation and status work together and form a common bitmask. Operation is just not reset, + // while status is. + uint8_t _operation = 0; + + // This information is filled based on the "device" and "device_id" during initialization: + uint8_t _bus = 0; + uint8_t _address = 0x74; + uint8_t _ports = 16; + uint8_t _invertReg = 0xFF; + uint8_t _operationReg = 0xFF; + uint8_t _inputReg = 0xFF; + uint8_t _outputReg = 0xFF; + Pin _interruptPin; + + RegisterSet _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Current register values: + RegisterSet _configuration; + RegisterSet _invert; + volatile RegisterSet _input; + volatile RegisterSet _output; + + // I2C communications within an ISR is not a good idea, it will crash everything. We offload + // the communications using a task queue. Dirty tells which devices and registers to poll. + // Every I2C roundtrip is always responsible for 8 bytes. + TaskHandle_t _isrHandler = nullptr; + + uint8_t _usedIORegisters; + std::atomic _dirtyWriteBuffer; + std::atomic _dirtyWrite; + + // Status is a bitmask that tells the task handle what needs to happen during the next roundtrip. + // This works together with 'operation'. + std::atomic _status; + ISRData _isrData[64]; + + static void isrTaskLoop(void* arg); + void isrTaskLoopDetail(); + + public: + I2CExtender(); + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + + void group(Configuration::HandlerBase& handler) override; + + static void interruptHandler(void* arg); + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~I2CExtender(); + }; +} diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp deleted file mode 100644 index eea3cb056..000000000 --- a/FluidNC/src/Extenders/PCA9539.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) 2021 - Stefan de Bruijn -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. - -#include "Extenders.h" -#include "PCA9539.h" -#include "../Logging.h" - -#include -#include - -namespace Extenders { - void PCA9539::claim(pinnum_t index) { - Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); - - uint64_t mask = uint64_t(1) << index; - Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); - - _claimed |= mask; - } - - void PCA9539::free(pinnum_t index) { - uint64_t mask = uint64_t(1) << index; - _claimed &= ~mask; - } - - uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { - Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); - - auto err = bus->write(address, ®, 1); - - if (err) { - log_info("Error writing to i2c bus. Code: " << err); - return 0; - } - - uint8_t inputData; - if (bus->read(address, &inputData, 1) != 1) { - log_info("Error reading from i2c bus."); - } - - return inputData; - } - - void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { - Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); - - uint8_t data[2]; - data[0] = reg; - data[1] = uint8_t(value); - auto err = bus->write(address, data, 2); - - if (err) { - log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); - } - } - - void PCA9539::validate() const { - auto i2c = config->_i2c; - Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); - } - - void PCA9539::group(Configuration::HandlerBase& handler) { - handler.item("interrupt0", _isrData[0]._pin); - handler.item("interrupt1", _isrData[1]._pin); - handler.item("interrupt2", _isrData[2]._pin); - handler.item("interrupt3", _isrData[3]._pin); - } - - void PCA9539::isrTaskLoop(void* arg) { - auto inst = static_cast(arg); - while (true) { - void* ptr; - if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { - ISRData* valuePtr = static_cast(ptr); - log_info("PCA state change ISR"); - valuePtr->updateValueFromDevice(); - } - } - } - - void PCA9539::init() { - this->_i2cBus = config->_i2c; - - _isrQueue = xQueueCreate(16, sizeof(void*)); - xTaskCreatePinnedToCore(isrTaskLoop, // task - "isr_handler", // name for task - configMINIMAL_STACK_SIZE + 512, // size of task stack - this, // parameters - 1, // priority - &_isrHandler, - SUPPORT_TASK_CORE // core - ); - - for (int i = 0; i < 4; ++i) { - auto& data = _isrData[i]; - - data._address = uint8_t(0x74 + i); - data._container = this; - data._valueBase = reinterpret_cast(&_value) + i; - - // Update the value first by reading it: - data.updateValueFromDevice(); - - if (!data._pin.undefined()) { - data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); - - // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. - data._pin.attachInterrupt(updatePCAState, FALLING, &data); - } else { - // Reset valueBase so we know it's not bound to an ISR: - data._valueBase = nullptr; - } - } - } - - void PCA9539::ISRData::updateValueFromDevice() { - const uint8_t InputReg = 0; - auto i2cBus = _container->_i2cBus; - - auto r1 = I2CGetValue(i2cBus, _address, InputReg); - auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); - uint16_t oldValue = *_valueBase; - uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); - *_valueBase = value; - - if (_hasISR) { - for (int i = 0; i < 16; ++i) { - uint16_t mask = uint16_t(1) << i; - - if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { - log_info("State change pin " << i); - switch (_isrMode[i]) { - case RISING: - if ((value & mask) == mask) { - _isrCallback[i](_isrArgument); - } - break; - case FALLING: - if ((value & mask) == 0) { - _isrCallback[i](_isrArgument); - } - break; - case CHANGE: - _isrCallback[i](_isrArgument); - break; - } - } - } - } - } - - void PCA9539::updatePCAState(void* ptr) { - ISRData* valuePtr = static_cast(ptr); - - BaseType_t xHigherPriorityTaskWoken = false; - xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); - } - - void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { - bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); - bool output = attr.has(Pins::PinAttributes::Output); - - uint64_t mask = uint64_t(1) << index; - _invert = (_invert & ~mask) | (activeLow ? mask : 0); - _configuration = (_configuration & ~mask) | (output ? 0 : mask); - - const uint8_t deviceId = index / 16; - - const uint8_t ConfigReg = 6; - uint8_t address = 0x74 + deviceId; - - uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); - uint8_t reg = ConfigReg + ((index / 8) & 1); - - log_info("Setup reg " << int(reg) << " with value " << int(value)); - - I2CSetValue(_i2cBus, address, reg, value); - } - - void PCA9539::writePin(pinnum_t index, bool high) { - uint64_t mask = uint64_t(1) << index; - uint64_t oldVal = _value; - uint64_t newVal = high ? mask : uint64_t(0); - _value = (_value & ~mask) | newVal; - - _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); - } - - bool PCA9539::readPin(pinnum_t index) { - uint8_t reg = uint8_t(index / 8); - uint8_t deviceId = reg / 2; - - // If it's handled by the ISR, we don't need to read anything from the device. - // Otherwise, we do. Check: - if (_isrData[deviceId]._valueBase == nullptr) { - const uint8_t InputReg = 0; - uint8_t address = 0x74 + deviceId; - - auto readReg = InputReg + (reg & 1); - auto value = I2CGetValue(_i2cBus, address, readReg); - uint64_t newValue = uint64_t(value) << (int(reg) * 8); - uint64_t mask = uint64_t(0xff) << (int(reg) * 8); - - _value = ((newValue ^ _invert) & mask) | (_value & ~mask); - - log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); - } else { - log_info("No read, value is " << int(_value)); - } - - return (_value & (1ull << index)) != 0; - } - - void PCA9539::flushWrites() { - uint64_t write = _value ^ _invert; - for (int i = 0; i < 8; ++i) { - if ((_dirtyRegisters & (1 << i)) != 0) { - const uint8_t OutputReg = 2; - uint8_t address = 0x74 + (i / 2); - - uint8_t val = uint8_t(write >> (8 * i)); - uint8_t reg = OutputReg + (i & 1); - I2CSetValue(_i2cBus, address, reg, val); - } - } - - _dirtyRegisters = 0; - } - - // ISR's: - void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { - int device = index / 16; - int pinNumber = index % 16; - - Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); - - _isrData[device]._isrCallback[pinNumber] = callback; - _isrData[device]._isrArgument[pinNumber] = arg; - _isrData[device]._isrMode[pinNumber] = mode; - _isrData[device]._hasISR = true; - } - - void PCA9539::detachInterrupt(pinnum_t index) { - int device = index / 16; - int pinNumber = index % 16; - - _isrData[device]._isrCallback[pinNumber] = nullptr; - _isrData[device]._isrArgument[pinNumber] = nullptr; - _isrData[device]._isrMode[pinNumber] = 0; - - bool hasISR = false; - for (int i = 0; i < 16; ++i) { - hasISR |= (_isrData[device]._isrArgument[i] != nullptr); - } - _isrData[device]._hasISR = hasISR; - } - - const char* PCA9539::name() const { return "pca9539"; } - - PCA9539 ::~PCA9539() { - for (int i = 0; i < 4; ++i) { - auto& data = _isrData[i]; - - if (!data._pin.undefined()) { - data._pin.detachInterrupt(); - } - } - } - - // Register extender: - namespace { - PinExtenderFactory::InstanceBuilder registration("pca9539"); - } -} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h deleted file mode 100644 index d90068370..000000000 --- a/FluidNC/src/Extenders/PCA9539.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2021 - Stefan de Bruijn -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. - -#pragma once - -#include "PinExtenderDriver.h" -#include "../Configuration/Configurable.h" -#include "../Machine/MachineConfig.h" -#include "../Machine/I2CBus.h" -#include "../Platform.h" - -#include - -namespace Pins { - class PCA9539PinDetail; -} - -namespace Extenders { - // Pin extenders... - // - // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address - // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 - // separate registers, so that's a total of 16*4 = 64 values. - // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf - // Speed: 400 kHz - // - // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single - // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. - // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf - // Speed: 400 kHz - // - // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means - // that *some* pin has changed state. We don't know which one that was obviously. - // However, we can then query the individual pins (thereby resetting them) and throwing - // the results as individual ISR's. - // - // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the - // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless - // to say, this is usually a bad idea, because things like endstops become much slower - // as a result. For now, I just felt like not supporting it. - // - // The MCP23017 has two interrupt lines, one for register A and register B. Apart from - // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, - // so that's a total of 8 * 16 = 128 I/O ports. - // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf - // Speed: 100 kHz, 400 kHz, 1.7 MHz. - // - // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 - // seems to be the same, but 8-bit. - // - // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state - // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes - // pullups and schmitt triggers. - // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf - class PCA9539 : public PinExtenderDriver { - friend class Pins::PCA9539PinDetail; - - // Address can be set for up to 4 devices. Each device supports 16 pins. - - static const int numberPins = 16 * 4; - uint64_t _claimed; - - Machine::I2CBus* _i2cBus; - - static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); - static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); - - // Registers: - // 4x16 = 64 bits. Fits perfectly into an uint64. - uint64_t _configuration = 0; - uint64_t _invert = 0; - volatile uint64_t _value = 0; - - // 4 devices, 2 registers per device. 8 bits is enough: - uint8_t _dirtyRegisters = 0; - - QueueHandle_t _isrQueue = nullptr; - TaskHandle_t _isrHandler = nullptr; - - static void isrTaskLoop(void* arg); - - struct ISRData { - ISRData() = default; - - Pin _pin; - PCA9539* _container = nullptr; - volatile uint16_t* _valueBase = nullptr; - uint8_t _address = 0; - - typedef void (*ISRCallback)(void*); - - bool _hasISR = false; - ISRCallback _isrCallback[16] = { 0 }; - void* _isrArgument[16] = { 0 }; - int _isrMode[16] = { 0 }; - - void IRAM_ATTR updateValueFromDevice(); - }; - - ISRData _isrData[4]; - static void IRAM_ATTR updatePCAState(void* ptr); - - public: - PCA9539() = default; - - void claim(pinnum_t index) override; - void free(pinnum_t index) override; - - void validate() const override; - void group(Configuration::HandlerBase& handler) override; - - void init(); - - void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; - void IRAM_ATTR writePin(pinnum_t index, bool high) override; - bool IRAM_ATTR readPin(pinnum_t index) override; - void IRAM_ATTR flushWrites() override; - - void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; - void detachInterrupt(pinnum_t index) override; - - const char* name() const override; - - ~PCA9539(); - }; -} diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index 7158661de..dcae580d3 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -4,6 +4,7 @@ #include "I2CBus.h" #include +#include namespace Machine { void I2CBus::validate() const { @@ -25,30 +26,56 @@ namespace Machine { auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + Assert(_busNumber == 0 || _busNumber == 1, "Bus # has to be 0 or 1; the ESP32 does not have more i2c peripherals."); + if (_busNumber == 0) { i2c = &Wire; } else { i2c = &Wire1; } - i2c->begin(sdaPin, sclPin /*, _frequency */); + i2c->begin(int(sdaPin), int(sclPin), _frequency); - log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); + log_info("I2C SDA: " << _sda.name() << ", SCL: " << _scl.name() << ", Freq: " << _frequency << ", Bus #: " << _busNumber); } + const char* I2CBus::ErrorDescription(int code) { + switch (code) { + case I2C_ERROR_OK: + return "ok"; + case I2C_ERROR_DEV: + return "general device error"; + case I2C_ERROR_ACK: + return "no ack returned by device"; + case I2C_ERROR_TIMEOUT: + return "timeout"; + case I2C_ERROR_BUS: + return "bus error"; + case I2C_ERROR_BUSY: + return "device busy"; + case I2C_ERROR_MEMORY: + return "insufficient memory"; + case I2C_ERROR_CONTINUE: + return "continue"; + case I2C_ERROR_NO_BEGIN: + return "begin transmission missing"; + default: + return "unknown"; + } + } int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { - // log_info("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " - // << (i2c ? "non null" : "null")); + // log_debug("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); i2c->beginTransmission(address); for (size_t i = 0; i < count; ++i) { i2c->write(data[i]); } - return i2c->endTransmission(); // i2c_err_t ?? + return i2c->endTransmission(); // i2c_err_t, see header file } int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { - // log_info("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " - // << (i2c ? "non null" : "null")); + // log_debug("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); for (size_t i = 0; i < count; ++i) { if (i2c->requestFrom((int)address, 1) != 1) { diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index febbf51fc..f7ddab35e 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -20,27 +20,15 @@ namespace Machine { int _busNumber = 0; Pin _sda; Pin _scl; - uint32_t _frequency = 0; + uint32_t _frequency = 100000; void init(); void validate() const override; void group(Configuration::HandlerBase& handler) override; - /* - typedef enum { - I2C_ERROR_OK=0, - I2C_ERROR_DEV, - I2C_ERROR_ACK, - I2C_ERROR_TIMEOUT, - I2C_ERROR_BUS, - I2C_ERROR_BUSY, - I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE, - I2C_ERROR_NO_BEGIN - } i2c_err_t; - */ - - int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! + static const char* ErrorDescription(int code); + + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); ~I2CBus() = default; diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 93d739c8d..5f2cffa0b 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -34,7 +34,7 @@ - + @@ -182,6 +182,7 @@ + @@ -218,7 +219,7 @@ - + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index d8f5ac631..72c058c0e 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -582,9 +582,6 @@ src\Machine - - src\Extenders - src\Extenders @@ -597,6 +594,12 @@ src\Pins + + X86TestSupport + + + src\Extenders + @@ -1016,9 +1019,6 @@ src\Machine - - src\Extenders - src\Extenders @@ -1031,6 +1031,9 @@ src\Pins + + src\Extenders + diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index aa9f01dcf..98cddae8a 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -1,6 +1,7 @@ #pragma once #include +#include "esp32-hal-i2c.h" #include "Stream.h" class TwoWire : public Stream { diff --git a/X86TestSupport/TestSupport/esp32-hal-i2c.h b/X86TestSupport/TestSupport/esp32-hal-i2c.h new file mode 100644 index 000000000..066108aad --- /dev/null +++ b/X86TestSupport/TestSupport/esp32-hal-i2c.h @@ -0,0 +1,13 @@ +#pragma once + +typedef enum { + I2C_ERROR_OK = 0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; From 292cf087627ea17446e8e60001ba5dc001c57be3 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Wed, 2 Mar 2022 15:07:40 +0100 Subject: [PATCH 004/100] Fixed a few small bugs. It's not working yet, but getting there... --- FluidNC/src/Extenders/I2CExtender.cpp | 5 ++++- FluidNC/src/Extenders/I2CExtender.h | 2 -- FluidNC/src/Machine/I2CBus.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 8a824a24f..4c5b32e69 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -13,6 +13,8 @@ #include namespace Extenders { + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { @@ -216,7 +218,7 @@ namespace Extenders { // device: pca9539 // device_id: 0 // interrupt: gpio.36 - handler.item("device", _device); + handler.item("device", _device, i2cDevice); handler.item("device_id", _deviceId); handler.item("interrupt", _interruptPin); } @@ -228,6 +230,7 @@ namespace Extenders { void I2CExtender::init() { Assert(_isrHandler == nullptr, "Init has already been called on I2C extender."); + this->_i2cBus = config->_i2c; switch (I2CExtenderDevice(_device)) { case I2CExtenderDevice::PCA9539: diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index b98ff6037..c94e25f99 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -21,8 +21,6 @@ namespace Extenders { PCA9539, }; - EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; - // Pin extenders... // // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index dcae580d3..daf5120a2 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -18,7 +18,7 @@ namespace Machine { void I2CBus::group(Configuration::HandlerBase& handler) { handler.item("sda", _sda); handler.item("scl", _scl); - handler.item("busNumber", _busNumber); + handler.item("bus", _busNumber); handler.item("frequency", _frequency); } From 04dabfa11ce4a210e0793d57896dd1b470da647c Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 12:23:26 +0100 Subject: [PATCH 005/100] Added first I2C extender unit tests --- FluidNC/src/Extenders/I2CExtender.cpp | 51 +-- FluidNC/src/Extenders/I2CExtender.h | 9 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 340 +++++++++++++++++++ UnitTests.vcxproj | 1 + UnitTests.vcxproj.filters | 6 + X86TestSupport/TestSupport/Capture.h | 2 +- X86TestSupport/TestSupport/Wire.cpp | 146 ++++++++ X86TestSupport/TestSupport/Wire.h | 92 ++--- X86TestSupport/TestSupport/freertos/Task.cpp | 1 + 9 files changed, 582 insertions(+), 66 deletions(-) create mode 100644 FluidNC/test/Extender/I2CExtenderTests.cpp diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 4c5b32e69..b7dea07c7 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -15,7 +15,7 @@ namespace Extenders { EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; - I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} + I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { int err; @@ -67,6 +67,7 @@ namespace Extenders { if (newStatus != 0) { if ((newStatus & 2) != 0) { + _status = 0; break; } @@ -81,34 +82,34 @@ namespace Extenders { claimedValues = 0; for (int i = 0; i < 8; ++i) { if (_claimed.bytes[i] != 0) { - claimedValues = i; + claimedValues = i + 1; } } - // Configuration: - { + // Invert: + if (_invertReg != 0xFF) { + uint8_t currentRegister = _invertReg; + uint8_t address = _address; for (int i = 0; i < claimedValues; ++i) { - uint8_t currentRegister = _operationReg; - uint8_t address = _address; - - uint8_t by = _configuration.bytes[i]; + uint8_t by = _invert.bytes[i]; I2CSetValue(this->_i2cBus, address, currentRegister, by); currentRegister++; - if (currentRegister == registersPerDevice + _operationReg) { + if (currentRegister == registersPerDevice + _invertReg) { ++address; } } } - // Invert: - if (_invertReg != 0xFF) { - uint8_t currentRegister = _invertReg; - uint8_t address = _address; + // Configuration: + { for (int i = 0; i < claimedValues; ++i) { - uint8_t by = _invert.bytes[i]; + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + + uint8_t by = _configuration.bytes[i]; I2CSetValue(this->_i2cBus, address, currentRegister, by); currentRegister++; - if (currentRegister == registersPerDevice + _invertReg) { + if (currentRegister == registersPerDevice + _operationReg) { ++address; } } @@ -116,7 +117,8 @@ namespace Extenders { // Configuration changed. Writes and reads must be updated. if (_outputReg != 0xFF) { - newStatus |= 4; // writes + newStatus |= 4; // writes + _dirtyWrite = 0xFF; // everything is dirty. } if (_inputReg != 0xFF) { newStatus |= 8; // reads @@ -248,6 +250,9 @@ namespace Extenders { break; } + // Ensure data is available: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + xTaskCreatePinnedToCore(isrTaskLoop, // task "i2cHandler", // name for task configMINIMAL_STACK_SIZE + 512, // size of task stack @@ -300,15 +305,19 @@ namespace Extenders { void IRAM_ATTR I2CExtender::writePin(pinnum_t index, bool high) { Assert(index < 64 && index >= 0, "Pin index out of range"); - uint64_t mask = 1ull << index; + uint64_t mask = 1ull << index; + auto oldValue = _output.value; if (high) { _output.value |= mask; } else { _output.value &= ~mask; } - uint8_t dirtyMask = uint8_t(1 << (index / 8)); - _dirtyWriteBuffer |= dirtyMask; + // Did something change? + if (oldValue != _output.value) { + uint8_t dirtyMask = uint8_t(1 << (index / 8)); + _dirtyWriteBuffer |= dirtyMask; + } // Note that _status is *not* updated! flushWrites takes care of this! } @@ -398,7 +407,9 @@ namespace Extenders { } // Give enough time for the task to stop: - vTaskDelay(TaskDelayBetweenIterations * 10); + for (int i = 0; i < 10 && _status != 0; ++i) { + vTaskDelay(TaskDelayBetweenIterations); + } // Should be safe now to stop. _isrHandler = nullptr; diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index c94e25f99..222226d53 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -144,18 +144,16 @@ namespace Extenders { static void isrTaskLoop(void* arg); void isrTaskLoopDetail(); + static void interruptHandler(void* arg); + public: I2CExtender(); void claim(pinnum_t index) override; void free(pinnum_t index) override; - void validate() const override; - void group(Configuration::HandlerBase& handler) override; - - static void interruptHandler(void* arg); - + void validate() const override; void init(); void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; @@ -164,7 +162,6 @@ namespace Extenders { void IRAM_ATTR flushWrites() override; void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; - void detachInterrupt(pinnum_t index) override; const char* name() const override; diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp new file mode 100644 index 000000000..2db277858 --- /dev/null +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -0,0 +1,340 @@ +#include "../TestFramework.h" + +#include +#include +#include +#include +#include + +#include "Capture.h" + +namespace Configuration { + Test(I2CExtender, I2CBasics) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + + bus.validate(); + bus.init(); + + Wire.Clear(); + + Assert(0 == bus.write(1, reinterpret_cast("aap"), 3), "Bad write"); + auto data = Wire.Receive(); + + Assert(data.size() == 3, "Expected 3 bytes"); + data.push_back(0); + Assert(!strcmp(reinterpret_cast(data.data()), "aap"), "Incorrect data read"); + + uint8_t tmp[4]; + tmp[3] = 0; + Assert(bus.read(1, tmp, 3) == 0, "Expected no data available for read"); + + std::vector tmp2; + tmp2.push_back(uint8_t('p')); + tmp2.push_back(uint8_t('i')); + tmp2.push_back(uint8_t('m')); + + Wire.Send(tmp2); + Assert(bus.read(1, tmp, 3) == 3, "Expected 3 bytes data available for read"); + Assert(bus.read(1, tmp, 3) == 0, "Expected no data available for read"); + Assert(!strcmp(reinterpret_cast(tmp), "pim"), "Incorrect data read"); + } + + // Helper class for initialization of I2C extender + class FakeInitHandler : public Configuration::HandlerBase { + bool hasISR_; + + protected: + void enterSection(const char* name, Configurable* value) override {} + bool matchesUninitialized(const char* name) override { return true; } + + public: + FakeInitHandler(bool hasISR) : hasISR_(hasISR) {} + + void item(const char* name, float& value, float minValue = -3e38, float maxValue = 3e38) override {} + void item(const char* name, std::vector& value) override {} + void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override {} + void item(const char* name, Pin& value) override { + if (!strcmp(name, "interrupt") && hasISR_) { + value = Pin::create("gpio.15"); + } + } + void item(const char* name, IPAddress& value) override {} + void item(const char* name, int& value, EnumItem* e) override { + if (!strcmp(name, "device")) { + value = int(Extenders::I2CExtenderDevice::PCA9539); + } + } + + void item(const char* name, String& value, int minLength = 0, int maxLength = 255) override {} + + HandlerType handlerType() override { return HandlerType::Parser; } + + void item(const char* name, bool& value) override {} + void item(const char* name, int32_t& value, int32_t minValue = 0, int32_t maxValue = INT32_MAX) override { + if (!strcmp(name, "device_id")) { + value = 0; + } + } + }; + + Test(I2CExtender, InitDeinit) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + } + + Test(I2CExtender, ClaimRelease) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + i2c.claim(1); + i2c.claim(0); + AssertThrow(i2c.claim(1)); + i2c.claim(2); + AssertThrow(i2c.claim(64)); + AssertThrow(i2c.claim(-1)); + i2c.free(1); + i2c.free(1); + i2c.claim(1); + AssertThrow(i2c.claim(1)); + i2c.free(0); + i2c.free(1); + i2c.free(2); + } + + class Roundtrip { + uint32_t before; + + public: + Roundtrip() { before = Capture::instance().current(); } + + ~Roundtrip() { + while (Capture::instance().current() < before + 1) { + delay(10); + } + } + }; + + Test(I2CExtender, SetupPin) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + // Expected register values (see datasheet): + // + // 4 invert + // 1 = invert, 0 = normal + // + // 6 config + // 1 = input, 0 = output + // + // 2 write + // 1 = high, 0 = low + // + // 0 read + // high = 1, low = 0 + + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x01); + + i2c.claim(0); + i2c.setupPin(0, Pin::Attr::Output); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), + uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Read will trigger an update, because we don't have an ISR + { + Wire.Send(0x01); + bool readPin = i2c.readPin(0); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test write pin: + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + } + { + // Write to set it 'low'. + i2c.writePin(0, false); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 0, "Expected write reg 0 = 0"); + } + { + // Write to set it 'low'. It's already low, no-op. + i2c.writePin(0, false); + i2c.flushWrites(); + // no-op. + } + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + } + + // NOTE: We ended with setting pin #0 to 'high' = 0x01 + + // Setup pin for reading: + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + + i2c.claim(1); + i2c.setupPin(1, Pin::Attr::Input); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), + uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Setup another pin for reading with an invert mask and a PU: + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x04); + + i2c.claim(2); + i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), + uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Test read pin: + { + Wire.Send(0x02); + bool readPin = i2c.readPin(1); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test read pin: + { + Wire.Send(0x02); + bool readPin = i2c.readPin(2); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == false, "Expected 'true' on pin"); + } + } +} diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 5f2cffa0b..f74585854 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -279,6 +279,7 @@ + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 72c058c0e..716078b48 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -49,6 +49,9 @@ {2b9b9202-4bb3-450e-a90b-99f042de0af2} + + {4f2af482-2e54-41bf-b698-1ab77a1a5ea3} + @@ -1034,6 +1037,9 @@ src\Extenders + + test\Extender + diff --git a/X86TestSupport/TestSupport/Capture.h b/X86TestSupport/TestSupport/Capture.h index f8d3be1df..e9ce30a7c 100644 --- a/X86TestSupport/TestSupport/Capture.h +++ b/X86TestSupport/TestSupport/Capture.h @@ -21,7 +21,7 @@ class Capture { Capture() = default; std::vector events; - uint32_t currentTime = 0; + volatile uint32_t currentTime = 0; public: static Capture& instance() { diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 046c93eda..69373b035 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -2,3 +2,149 @@ TwoWire Wire(0); TwoWire Wire1(1); + +TwoWire::TwoWire(uint8_t bus_num) {} +TwoWire::~TwoWire() {} + +// For unit tests: +void TwoWire::Send(std::vector data) { + for (auto it : data) { + Send(it); + } +} +void TwoWire::Send(uint8_t value) { + receivedData.push_back(value); +} +std::vector TwoWire::Receive() { + std::vector data; + std::swap(sentData, data); + return data; +} + +void TwoWire::Clear() { + sentData.clear(); + receivedData.clear(); +} + +// TwoWire interface: + +// call setPins() first, so that begin() can be called without arguments from libraries +bool TwoWire::setPins(int sda, int scl) { + return true; +} + +bool TwoWire::begin(int sda, int scl, uint32_t frequency) { + return true; +} // returns true, if successful init of i2c bus + +bool TwoWire::begin(uint8_t slaveAddr, int sda, int scl, uint32_t frequency) { + return true; +} +bool TwoWire::end() { + return true; +} + +void TwoWire::setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms +uint16_t TwoWire::getTimeOut() { + return 0; +} + +bool TwoWire::setClock(uint32_t) { + return true; +} +uint32_t TwoWire::getClock() { + return 0; +} + +void TwoWire::beginTransmission(uint16_t address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} +void TwoWire::beginTransmission(uint8_t address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} +void TwoWire::beginTransmission(int address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} + +uint8_t TwoWire::endTransmission(bool sendStop) { + Assert(inTransmission, "Should be in a transmission"); + inTransmission = false; + return 0; +} +uint8_t TwoWire::endTransmission(void) { + Assert(inTransmission, "Should be in a transmission"); + inTransmission = false; + return 0; +} + +size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { + auto available = receivedData.size(); + if (available > size) { + available = size; + } + return available; +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop)); +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop != 0)); +} +size_t TwoWire::requestFrom(uint8_t address, size_t len, bool stopBit) { + return uint8_t(requestFrom(uint16_t(address), size_t(len), stopBit)); +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size) { + return uint8_t(requestFrom(address, size_t(size), false)); +} +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop != 0)); +} +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t size) { + return uint8_t(requestFrom(address, size_t(size), false)); +} +uint8_t TwoWire::requestFrom(int address, int size, int sendStop) { + return uint8_t(requestFrom(uint16_t(address), size_t(size), sendStop != 0)); +} +uint8_t TwoWire::requestFrom(int address, int size) { + return uint8_t(requestFrom(uint16_t(address), size_t(size), false)); +} + +size_t TwoWire::write(uint8_t ch) { + Assert(inTransmission, "Should be in a transmission"); + sentData.push_back(ch); + return 0; +} + +size_t TwoWire::write(const uint8_t* buf, size_t size) { + for (size_t i = 0; i < size; ++i) { + write(buf[i]); + } + return size; +} +int TwoWire::available(void) { + return receivedData.size(); +} +int TwoWire::read(void) { + if (receivedData.size()) { + auto result = receivedData[0]; + receivedData.erase(receivedData.begin()); + return result; + } else { + return -1; + } +} +int TwoWire::peek(void) { + if (receivedData.size()) { + auto result = receivedData[0]; + return result; + } else { + return -1; + } +} +void TwoWire::flush(void) {} + +extern TwoWire Wire; +extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index 98cddae8a..1bda1ea24 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -1,50 +1,64 @@ #pragma once #include +#include #include "esp32-hal-i2c.h" #include "Stream.h" +#include "../FluidNC/test/TestFramework.h" class TwoWire : public Stream { + bool inTransmission = false; + std::vector receivedData; + std::vector sentData; + public: - TwoWire(uint8_t bus_num) {} - ~TwoWire() {} - - //call setPins() first, so that begin() can be called without arguments from libraries - bool setPins(int sda, int scl) {} - - bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } // returns true, if successful init of i2c bus - bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } - bool end() { return true; } - - void setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms - uint16_t getTimeOut() { return 0; } - - bool setClock(uint32_t) {} - uint32_t getClock() { return 0; } - - void beginTransmission(uint16_t address) {} - void beginTransmission(uint8_t address) {} - void beginTransmission(int address) {} - - uint8_t endTransmission(bool sendStop) { return 0; } - uint8_t endTransmission(void) { return 0; } - - size_t requestFrom(uint16_t address, size_t size, bool sendStop) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { return 0; } - size_t requestFrom(uint8_t address, size_t len, bool stopBit) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t size) { return 0; } - uint8_t requestFrom(int address, int size, int sendStop) { return 0; } - uint8_t requestFrom(int address, int size) { return 0; } - - size_t write(uint8_t) { return 0; } - size_t write(const uint8_t*, size_t) { return 0; } - int available(void) { return 0; } - int read(void) { return 0; } - int peek(void) { return 0; } - void flush(void) {} + TwoWire(uint8_t bus_num); + ~TwoWire(); + + // For unit tests: + void Send(std::vector data); + void Send(uint8_t value); + std::vector Receive(); + void Clear(); + + // TwoWire interface: + + // call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl); + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0); // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0); + bool end(); + + void setTimeOut(uint16_t timeOutMillis); // default timeout of i2c transactions is 50ms + uint16_t getTimeOut(); + + bool setClock(uint32_t); + uint32_t getClock(); + + void beginTransmission(uint16_t address); + void beginTransmission(uint8_t address); + void beginTransmission(int address); + + uint8_t endTransmission(bool sendStop); + uint8_t endTransmission(void); + + size_t requestFrom(uint16_t address, size_t size, bool sendStop); + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); + uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop); + size_t requestFrom(uint8_t address, size_t len, bool stopBit); + uint8_t requestFrom(uint16_t address, uint8_t size); + uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop); + uint8_t requestFrom(uint8_t address, uint8_t size); + uint8_t requestFrom(int address, int size, int sendStop); + uint8_t requestFrom(int address, int size); + + size_t write(uint8_t ch); + size_t write(const uint8_t* buf, size_t size); + int available(void); + int read(void); + int peek(void); + void flush(void); inline size_t write(const char* s) { return write((uint8_t*)s, strlen(s)); } inline size_t write(unsigned long n) { return write((uint8_t)n); } diff --git a/X86TestSupport/TestSupport/freertos/Task.cpp b/X86TestSupport/TestSupport/freertos/Task.cpp index 8dd60b5c6..05036f34c 100644 --- a/X86TestSupport/TestSupport/freertos/Task.cpp +++ b/X86TestSupport/TestSupport/freertos/Task.cpp @@ -27,6 +27,7 @@ BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode, void vTaskDelay(const TickType_t xTicksToDelay) { Capture::instance().wait(xTicksToDelay); + delay(xTicksToDelay); } void vTaskDelayUntil(TickType_t* const pxPreviousWakeTime, const TickType_t xTimeIncrement) { From 33ef0988e1820fdff9ece59dee89b1b886495d80 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 14:46:09 +0100 Subject: [PATCH 006/100] Fixed a few bugs. According to the tests this should pretty much work. --- CodeCoverage.cov | 672 +++++++++++++++++++++ FluidNC/src/Extenders/I2CExtender.cpp | 71 ++- FluidNC/src/Extenders/I2CExtender.h | 8 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 474 ++++++++++++++- FluidNC/test/Pins/GPIO.cpp | 55 +- X86TestSupport/TestSupport/SoftwareGPIO.h | 2 +- X86TestSupport/TestSupport/Wire.cpp | 29 +- X86TestSupport/TestSupport/Wire.h | 9 + 8 files changed, 1251 insertions(+), 69 deletions(-) create mode 100644 CodeCoverage.cov diff --git a/CodeCoverage.cov b/CodeCoverage.cov new file mode 100644 index 000000000..a3f4cc636 --- /dev/null +++ b/CodeCoverage.cov @@ -0,0 +1,672 @@ +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.cpp +RES: _______cccccc_uuuuuu_uuuuuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_ccccc_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___ccccccccccccccc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\LegacySettingRegistry.h +RES: ____________uuuu______________u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\FS.h +RES: ________________________________________u________u_____________________c___________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10vSpindle.cpp +RES: ______________________uu__uuu__u_uuuu_uu_____u_u_u_uu__uu__u___uuuu_uuu__u__uuuu_u_uuuuu_uuuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StandardStepper.cpp +RES: _______________________u_uuuu_uu_uuuu_u_uu_uu_u_uuuuuuuuuuu_uuuu_uuu__uuuuuuuuuuu__uu_uuu_uuuuuu_u_uuu_u_u_u___c__uuuuuuu__uuuu___u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.h +RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________ccccuuuu__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Authentication.cpp +RES: ___________________________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.cpp +RES: ____cc_c__ccccccccccccccc_cccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.cpp +RES: _______uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPI.h +RES: ____uu___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\InputBuffer.h +RES: ____________u__________u_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SDFS.cpp +RES: ____cuuuuuuuuu_c__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.h +RES: ___________c______________c_c_c_________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.cpp +RES: ______________________________u_u_uuu_uuu__u_u_uuu_uuu___u_uuuu_uu_uu_uuu_____uuuuuu__uuu___uuuuuuuu_uuu_________________________________________________________________________________________uuu___uuu__u_uu___u_uuuu_uuu_uu_uu__uuu___uuuu_u_u_uu___u_uuuuuuuu__uuu_uu_uuuu_u______uuuuuu_uuuuu_____uuu_uuuuu_uuuuu_uuuuuuuuu_uuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPIFFS.cpp +RES: ____cu___uuuuuuu__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.cpp +RES: ____________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.cpp +RES: ________c_u____________cccuucc___u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.cpp +RES: ______________________________________ccccccc_u_u__uuuuuuu_uuuuu_u_uuuu_uu_uuu_ccc_uuu_uuu_ccc_ccc_cccuu_cc_ccuuc_c_uuuuu_uu_uuuuu_u_uuu_uuu_uuuu__uuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___c_c_c__cu___cc_cc_cc_u_u_u__uu___uuu_uu_uu_uu_uu_uu_uu_uu___uuu___uuuu_u__uuu__uu___uuuuuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.h +RES: ___________________________________________________________________________________c______c______c________u__cccc__uu_cc___cccc____c_c_u_uu_c____c_c__u_c___u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\rmt.cpp +RES: ____uuu_u_uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.h +RES: __________________________________________cc____ccu_cc_u______u_______________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\SPIBus.cpp +RES: __________uuuuu_u_uuu_uuu____uuuuu_u_uuuuu__u_________u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\nvs.cpp +RES: __uuuuuuuuu_uuuuuu_uuuuuu_uuuuuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\BTConfig.h +RES: __________uuu_u__________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\nvs.h +RES: ____________uuuuuuu_u_uuuuuuu_u_uuuuuuu_uuuu_uuu_u_uuuuuuu_uuuu_uuu_u_u_uuuu_u_u_uuuu__________uuuuuuu______________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Report.cpp +RES: ________________________________________________uuu_u_u__uuuuuuuu__uuuuu_u________uuu_uu____uuuuuuu__uuu_uu________uu____u_uu_u_u_uu_uuu__u_u_u_c______________c______uuuu_u___uu__________u__uu____u___uuuuu__u_uuuuu_uuu_uuuu_uuu_u__uuu_u_uu_uu_uu_uu_uu_uu_uu_uu_u__u_u_u_uu_uu_u__u_u_uu_u__u_u_uu_u__u_________u_uu_u__u__u_uu_uu_uu_uu_u__u_u_uu_uu_uu_u_u__uuuu_uu_uu___uu__uuuuuu__uuuuu_uuu______uuu_uu_uu___u_u_uuu_uuu_u___uuu______uuuuu_u_uu_uuu_uuu_uuuuuuu_uu_u_u_uu__u_u__u_u_uu_uu__u_u_uu_uuuuuu_u__uuuuuuuu_u_u__uuuuu_u_u______uu__uuuuuu_u___uu__u_uuuuu_____uuu_u_u_uuuu_____u_u__uu_uu__uuuu_____u_u___uuuuuu_u_uu_u_____u_uu_uu___uu_______uu_u__uuuuu_uu_u_______u_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\I2SO.cpp +RES: ____uuu_u_u_uuu_uuu_u_uuu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\FS.cpp +RES: _________________________uuu__uu_uuu__uu_uuu__uu_uuu__uu_uuu___uu__uu_uuu__uu_uuu__uuuuu_uuu__uu_uuu__uu_uuu__uu_uuu__uu_uuuu_u_u_uuu__uu_uuu__uu__uuu_uu_uuu_uu_uuu_uu_u_uuu__uu_uuu_uu_u_uuu_uu_u_uuu_uu_u_uuu_uu_u_uuu_uu_u_u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\LegacySettingRegistry.cpp +RES: ________u_u_uuu_uu_uuuu_uu_uuu_uuu_u_uuuuuu__u_uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Task.cpp +RES: _____________c_______ccccc_cccp_uuu_uuuuu_uuu_uuu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Capture.h +RES: _______________________c__cccc___uuuuuuuu__________________________ccuuu_u_c_____uuuu_____________uuuuuu_u_uu_uuuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spindle.h +RES: ______________________________uu____________uu__u___u__uu___uu_u_______u_u___uuuu_uu_u__u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Queue.cpp +RES: ______uuuuuuu_uu_uu_uuu_u_uuu_u____uu_uuu_uu_uuuu_u_uu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\ExceptionHelper.cpp +RES: _______________________________________cccc__cc__ccu__cccc_______c_cc____ccc_ccccccccccccccc__ccccccc_c__cu__cccccccc__ccc_c_________ccccccc_cccc_c__cccc_ccuc_uc_c__ccccuc_cc__cu__cu_c_u_uc___uuuuuu_u__u___u______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\esp32-hal-timer.cpp +RES: _________uuuu_uuu_uuu_uu___uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\LimitPin.h +RES: _____________u_____uu___u_________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.cpp +RES: __________c_ccu_uuuu_u_uuu___uu_u__uuu_uuuuu_u_uuu_u_c_c_u_u_uu_u_uu_uuuu_c_cuucuuuu_ccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\GPIO.cpp +RES: _____________________________uu__uu__u_u__u_________pu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu_uu_uu_uu_uuuuuuu______________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu_p_p_p_pu_uu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uu_uuuuu_u_uuuu_u_uuuuu_pu_uuu_pu_uuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu______uu_uu_uu_uu_____________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu__p_p_p_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\LedcPin.cpp +RES: ______________________u________uuuu_uu_u_u_u____uuuuuu_uuu____uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StandardStepper.h +RES: _________________u__________________uuuuu__u_____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10vSpindle.h +RES: ___________________________________u_uuuuu__u_u_________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-internal.h +RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________p____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StepStick.cpp +RES: __________u_uuu_uu__u_uu_uuuuu_uuu__uu_u__u___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Regex.cpp +RES: _____________________u_uu_uuu__uuu_uu_uu_uu_uu___uuu__uu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\IPAddress.h +RES: ____________________uccccccu_u_uu___u_________________u__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\GenericFactory.h +RES: ____________cccc__________c_____u______c_____c_u__uuuuuu_u_uuu_uuuuuuuu_u_uuuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\xmodem.cpp +RES: ________________________________________u_uuuuuu______________________u_uuuuu_____________uuuuuuu_uuuuuu__uu_uuuu______________uu_u_uuu_uuu_uuuuuu_uuuuuuuu_u___uuuuu_u__uuuuu_uu_uu_uuuuu_uuuuu______uuuu_uuuuuu__uuuuuuuuuu_uuuuu_uuuuuuu_uu__uuuuu_uu__uuuu___uuu_uu_uu_uuuu______uuuuuu____uu____uu_uuuuuuuuuuuuuuuu_uuuuuu_uuu_uuuu_______uuuuuuuuuuuuuu_uuu_______________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Channel.h +RES: ______________________________c__c__u__u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.h +RES: ____________________________cccc________________uuu______u__uuuuu______uuu_u_u_uuuuu_uuu_______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spindle.cpp +RES: ________________u_uuuu_uuuuu_u_uuuuu_u__uu_uuu_uuuu_________uuu_uuuu__uuu__uuuuu_uuuu_u_uuuuu_uuuuuuu_uu_uuu_uuuu_uu_u_uuu_uu_______uu___uuuuu___u_u_uu_u__u___u_u__uu_uuuu_u_uu___u_u__uu_uuu_uuuu____uu_uu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.cpp +RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__cuuuuuu_uuuuuuuuu_uu_uc__cc_u_c_uuu_uuuuuuuu_c_____c__c___c__c_ccuu___cucu___cu__cc_cccc_cccc_cccu_cu_cu__cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.cpp +RES: ____________uu_uu__u__uu_u_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\RcServo.h +RES: ________________u_____uu_uu_u_u______________uuuuuu__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\DebugPinDetail.cpp +RES: ___________u___uuuuuuuuu__uuuuu__uu_uuuu_uuu_uuu_uu_uu_uu_uu_uu_uu_u_uu_uu_u_uuuu_uu__uuuu_uu_uuu_u_u_uuuuuuuuuuuuuuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\VFDSpindle.cpp +RES: _________________________________________u____uu________________u__u___uu__u_uuu__uuu_u__u_uu_u___u_uu_uuu_u__u_____uuuu_uuuu___uuuu____uu_________uu_______u__uuuu_________uu_uuu__uuu___uu__u_uuu__u___u___uu__uu_uu_u_u__uuu__uu___u______u_uuuuu_uuuu__uu___uuu___uu__u_uuu_____u_u__uuu_________u_uu____u_u_uuuu__u_uu_u_u_uu_u_u__uuu____u_uu_uuu_u__________u__uuuu_____uuuu__u_uu__u_u_u_uuu___uuu_uuu_uuuuu__u_uuu__u_u_uuuuu__u_uu_uuuuu__u_uuuu_u_____u___u_uu_____uuuu_uuuuuu_uu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\MotorDriver.cpp +RES: ____________________________uuu_u_u_uuuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.h +RES: ______________________________________u____u__uu__uc____________cc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.cpp +RES: _____________u_uuu_uu_uuuuu____uu__u_uu_uuuu_u_u_____________uu_u_uu__uuuu_uuuuu_uuu_u_uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.h +RES: ____________________u_______uuuuuuuuu_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\RelaySpindle.cpp +RES: _______________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SDCard.h +RES: ________________________________________________________________________uuuu___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\NullSpindle.cpp +RES: _________________uuuuuuuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\uartdriver.cpp +RES: ________uuu_uu_uuuuuu_uuu_uuuuuu_uuuuuuuuuuu_uuuuuuuuuuuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\RelaySpindle.h +RES: ______________________u____u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SDCard.cpp +RES: _____________u_______uu_u_u__u_u___uuu___uu__uu___u_u_u__uuu__uu_uuu_uuu_uu_uuu_uuuu_u__uuuuuuu_u___uuu_u___u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WifiServices.cpp +RES: _________c_cu_uuu____________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\YL620Spindle.cpp +RES: ____________________________________________________________________________u_uuu__uuu_uu_uu_uu_u__u_u____uu_uuuuuu_uuuu_uuuuu___uuu_____uuuuu_uuuuu___uuu____u_uu_u__uu________uuuu_u_uuu__uuuuu___uu_u_uuuu_uuu__uuuuu_____uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\YL620Spindle.h +RES: __________uu_______u_uu__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\CoolantControl.cpp +RES: ______u__uuuu__uu_uu__uu_uu_uu___uu_uu___uu_uuuu__uuu_u___uuuu______uuu_uuu_uuuu_uuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\FreeRTOS.h +RES: ____________________________uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Serial.cpp +RES: ________________________________________________________________u_uuuuuu_u_____uu__uu_uuu_uu_uu_uu_uu_uu_u____u_uu_uu_uuu_u_uuu_u_uuu_u_uuu_u_uu_uu_uu_uu_uu_uuu_u_uuu_u_uuu_u_uuu_u_uu_u__________u__uuu_uu____u_uuuuu_uuuuuu_uuuuuuuu_uuuuuuuuuuuuu____u_uuu_u_uu_uuu_c_u_____uu_uuu_u_u_uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Serial.h +RES: ________________________________________________________________________c_________uuuu___u_______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ControlPin.h +RES: _____________u___uuu____u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Control.cpp +RES: ________uuu_uuuuuuuuuu_uuuuuuuuuu_uu_u_uu_u__u___uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\dac.cpp +RES: ______uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.h +RES: _________________________u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\LimitPin.cpp +RES: _____________uu_u_uuuu_uuuu_uuuu_uuu_____uuuu_uuuu___________uu__u_uuuuu_uu_uuu_uu__u_uuu_uuuuu_uu_uu____uuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.h +RES: ____________________________________c___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinCapabilities.h +RES: __________________c_____________________________cccc___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.cpp +RES: _____u_u_uu_u_u_u_uu_u_uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Probe.cpp +RES: _________u__uu_uuu__u_uuu__uuu____uuu_u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Laser.cpp +RES: _______________uuu_uu_u__u_u_u_uu_u_u__uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Laser.h +RES: ____________________________uu_u_u___uuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Generator.cpp +RES: ____________uuu_uuuuu_uuu_u_uuuu__uu_uuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\MyIOStream.h +RES: _________uuuu_cccc_cccc_cccc_cccc______uuuu_uuuu_uuuu_____u_u_________u__uuu_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Generator.h +RES: ____________________u_uuuuuu_______uu____uuuu_uuuu_uuuuuuuuuuu_uu_uuuu_uu_uu_u__u_uu_uu_u__uu_uuuu_uuuuu_uuuuuuuuuuuuuuu_uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\ledc.cpp +RES: _________cuuuuu______u__c_uuuuuuuuuuuuuuuuuu_uu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Logging.cpp +RES: ___________ccc_______ccc_cccc_ccc +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Jog.cpp +RES: _____________u__uuuu_u__uu__uuuuu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StackTrace\AssertionFailed.cpp +RES: __________________________________________c_cccc___ccccc_cc__cc__c___u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Planner.cpp +RES: ________________________uuu_uu____________uuuu_uu__uuu_uuu__________________________________________________________________u_u_uu______u_uuu_uu_uuuuu_uu__uuuuuu__u___uuuuu___uu_uuu______uu_uuu_uuuu_uuuuuu__uuu_uu_u_u__uuu__uuu_uu_uuuu_uu__uuu___uuuuuuu_uu__uu_uu___u_uuuu__uu_u__uu__uuuuuuuuuu____u_uuuuuuu_____uuuu_uu___u____uuuu_uu_u____uu______uuu_uuuuuu___u__uuu______________________uuuuu_u_uuu_uuuuuu______uuuu_uu_uu_u_uu__u__uuuuuu___uuuuu_u___u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.h +RES: _______________c__uuuu_u_u_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WifiConfig.h +RES: __________________uuuuuuu_uu_______________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\VFDSpindle.h +RES: _________________________uuu_____________________________________uuu_u__uu______u______________uuuu_uuu_uu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.cpp +RES: ____________________________uuuu__u__uuu_u_u____u__uu_uu_____u_uuu_uuu___uu__u_____u_uu__uu_u___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WebSettings.cpp +RES: ______________________________uu_uuuuu_______uuuuuu__uu_uu__u_uuuu_uu_u_uuuu__uuuuuuuu_u_u_uuu__uuu_uuu_uu__uu_uuu___u_u__uuu_uuuuu_uuuu_uuuuu_______________uuuuu_uuuu_uuuuuu_uuuuuuu__u_u_uuu_uuu_u__uu_uuuuu_uuu_uuuu__uuuu__uuu_uuu_uu_uuuu_uuu__uuuuu_uuu__uu___uuuuu_uuuu_uuuuuu_u_uuu_uuu_uu_uuuu_uuu_uuu_uuuuu_uuu_uuuu_uuuu_uuuuu_u_uuu_uuuuu_uuuu_uuu_uuuuu_uuu_uuuuu_uu_uuu_uuuuuuuuu_uu_u_uu_uu__uuuu_uuuu_uuuuuuuuuuuuuuuu_uuuu_uuuuuuuuuu_uuu_uuu_uu_u_uuu_uuu_uuu_uuuuu_uuu___uu__uu_uuu_uuuuuuuuu_uuuuuuuuuuu_u_uuu_u_____________________u_uu____uuuu_uuuuuuu_uuuuu_u_uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Settings.h +RES: _______________________________________________________________________uuuuu_________u______u________u_________u___u_uu__u_________uu_uuuu_u________uu___u____u__________________________________________u________u_u_u_u_uu_u_u____________________________________u__c______________________________________u____________________________________u_______________uu______________uu___________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\JSONEncoder.h +RES: _______________________________________u___________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Kinematics.h +RES: ___________________________________u______________u___________________uuu_____u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Macros.h +RES: ______________________uuu_uuu____uu_uu_uuu_uuu___uuu_____uuuuuuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Dynamixel2.cpp +RES: ______________________________________uuu_uuuuu_uu___uu_u_u_uuu__uu__uuu_uu_u_uu_u_uu_uuuuuu_uuu__uu_u__uu_uu__u_uu_uuuu_uuu__u___uu_u_uuuuu_uuu__u_u___uuu__uu_uuuu_uu_u_____u_uu_u_u_uu__u_uu_u_u_u_uuu_u_uu_uuuuu_uu_uu_uuuuu__uuuu_uuuu_u____u_uuuuu_u_uu_uuu_uu_uu_uu_uu_uu_uu_u____u_u_u_u___u_uu__u__u_uuuuuu_u__________uu_uuu__uu_uu___u_uu_uuu__u__uuuuuuuuuuuuuuuu__uuuu_uu___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinDetail.cpp +RES: _______uu_uuuu_uu_uu_u__uuuu_uuuuu_uuuu_u___uuuu_u_u_uuuuuu__u__uuu_u_uuuuuuuu_uuuu_uu_uuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\SPIBus.h +RES: ___________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\InputBuffer.cpp +RES: _______c_c_uuuu_uuuu_u_u_u_uuuuu_uuuuuuu_uuu_uuuuu_uuuuuu_uuuuuu_uuuu_u_u__u_uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\MachineConfig.cpp +RES: _________________________________uuuu_uuuuuuuuuuuuu_u_uuuuuuu_uu_uuuu__uu__uu__uu__uu__uu__uu__uu_____uu__uu__uu____u_uuuu_u_uu_u___u___uuuuuu__uuu__uu_uuu_uuuu__uuuuuu_uuuu_uuuu_uuuuu____uuu_u_u_u_u_uuuuu_u_uuuuu___u_uu__uuu_u_uuu_uuu_uuu_u_uu_cccccccccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepping.h +RES: _______________________u________________________u_uuuu_u__________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\NullSpindle.h +RES: ______________________________u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\HandlerBase.h +RES: _______________________________________uuuuu_uuuuu_______________pp_ppp_puu__p__uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StringRange.h +RES: ______________u_c_c____u__________uuuu_uuu_uu__uuuuuuuuuu__uu_uuuu_uu_uu______cccccc_c__cu_c_ccuucccccc_c_u_uuu_u_uuu_u_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\CoolantControl.h +RES: _____________u______uu___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Probe.h +RES: ___________________u______u______u___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\MachineConfig.h +RES: __________________________________uu_____u__u_uuuuu__u_____ccccccccccccc_c_cccc_c_____c__c_ccc_u_uu______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\MotionControl.cpp +RES: ______________________________uuu______________uu_u__uuu_________________uu___uuuuu_u__uuu_uuu_uuu_u____uuuu________________uuuuuuu_u_uuuu__uuuu_uuu_______u_u___uuu_u_uuuu__________________________uuu____uuuu_uuuuu__uuuuu__uuuuuuuuuuu_uu_u__uu__uuu_uuu____uu__u_____uu___u_uuu____uu_uu_______uuuu__uu__uuu__u__uuuu__uuuuu__uu_u_u_uuuuu_u_u___uuuuu_uu_uu_uuuu_u_uuuu_u___uuu_uuuuuu_uuu_uuuuu_u_u_uuu_uu_______u_uu_____u_uuu_uu_u_u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\JSONEncoder.cpp +RES: ________u________u______uuuu_uu____uuu_uu__uuuuu_____uuu_uu__u__uuuuuu_u__u___uuuu_u__uuuuu__uuuuuu__uuuuu___uuuuu__uuuuu__uuuu__uuuu__u___uuu_____uuuuu___uuu___uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Dynamixel2.h +RES: _________________________________________________u_____________________________________uu_____u_________uuuuu_uu_uu_u_u_uuuu__u_u__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinDetail.h +RES: _______________u___________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Main.cpp +RES: _____________________________________uuuu_uuu_uuu_u_uu_uuuuu_uuu_u___uuu___uuu___u_u____________________________________________u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.cpp +RES: _______cu_u_uu_uuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.h +RES: ________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Limits.cpp +RES: __________________u________________u_____uuu_uuuuu_uu____uuu_uuuuuu_uu__uu____uu_uu_uuuuuuu_uu___uu_uuuu_u_uuuu_u_______________________uuuuu__uu_uuuuu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinCapabilities.cpp +RES: ______c__________ccccc_cccc_cccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Protocol.cpp +RES: __________________________________c_____________c_________________________________________uuu_uuuu_u_uuuuuuuuuuuuuuuu___u________u___uuu_uuuu___uuuu_uuuu__u_________uu_uuu__uuuuuu_u_uuuuu_u_uu___uu_uu____u_uu__uuuu_____________uuu_u_u___u__uuuuu_uu_______uuu_u____________uuuu_u_uuuu____uu_u___uuuu______uuu_uu__uu_uuuuu_u_uuu_u_uuuu_u__uu____u___u__uu__uu__uu_u_______uu_u_uuu_____u___u__uu__uu__uuu_uu_u____uuu_u___uu__u_uu_u_uuuuuu__uuuu_u_uu_uu_uu__u_uu___uu_uuu__uuu__uu___u__________uu_u_uu_u_uuuuuuuuu_u___uu____u_uuuu_____u__u_uu__uuuuu____________u_uu_uu_u_uu_u_uu___uu__uu__u_uu_u_u________u__uuu_uuu___________uuuuuu_uuuuuuu___u_u_uuuuuu___uuuu___uuu___uu_uuuuu______uuuuuu__uu_uuuu__u___u_uuu__u__u_u__u_uu_u_u_uuu__uu_uu_____________________________uuu_u_uu__uuu__uu__uu__uu__uu__uu__uu__uuu_uuu_uuu_uuu__u________u_____u_____u__u______u__u_uuuuuu_u___uuuuuuuu_uu__uuu____uu__u__u_uu_u__uuuu____u__uuuuuuu__uuuuuuuu_uuuu_u__uuu__uuuuu_uuuuuuuu__uuu___u__u_uuuu___u_uu_uuuu___u_uuu____u_u___uuuuu__uuu___u__u_uuuuuuuuu_uuuu_uuuu__uu_u_u__uuu____uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axes.h +RES: _______________u___u_____________u____u_______uuu_uuuu_uuuu_______________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\lineedit.cpp +RES: _____ccc___uuu_uuuuuuuu_u_uuuuuuuuu_uuuuuu_u_u_uuuuuuuuuuuu_u_uuuuuuu_u___uu_uuuuu__uuuuu_u____uu_uuuuu__uuuuuuuu_uuuuu_u_uuu_uuuu_uuu_u___uuuu__uu_uu_uu_uuu_u___u____u_uu_uu_uuuuuu_uuuu_uu_uu_uuuu_u_uuuu_u_uuu_u_uuuu_uuuu_uuuuu_uuuuuuuuuuuuuu_uuu___uuuu_uuuu_______u__uuu__uuu_uuuuu_uuuuuuu___uuuuuuuuuuuuuu_uuu_uuu_uuu__uuu_uu_uuuuuuu__uuuu_uuu_uuuuuuu_uuu__uuuu_uuuuuuuuuuuu_uuuuuuuuuu__ccccccc_uuu_uuuuuuuu_u___uuuuuu________uuu_u__u_u_uu_uu____uu_u__uu__uuuuu_uu_uuu___uuu________uuu_u_uu_uu_u__uu___uuu__uu_uu_uu___uu_uu_uu_uu_uu_uu_uu_u______uuuuuuu____uuuuu_u_uuu___u_uu__uu_u__uuu__uuu__uu__uuu_u__uuu__uu_uu_uu_uu__uuu__uuu_uuuuu__uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\lineedit.h +RES: ______________________________c__________c___c_c__c_______________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Parser.cpp +RES: ______________c_u_uuuu_u_ccc_ccc_ccc_cc_c_uuuu_uu_uu__uu_uuu_uu_uu_uu_uu_uuu_uuuuu_uuuuu_uuuuuu_uuuu_uuuuu_uu_uuuuu_uuu_uuu_uuuu_u_u__uu__uu__uu_u___uuuuuuuuu__uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\WallPlotter.cpp +RES: _____________________uuuu_uuu_uu_uu______uuuuu_u_uu_u_u___________u_____uuu___uuuuu__u_u__u_uu___uu_uuu____u_______________uuu__uuu_u__uu_______u___u__uuu__u_______________________uuu__uu_uu__uuu__uuu_u__uuu_uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\MotorDriver.h +RES: ______________________________________u____________u_____________________________u__________u_____u__u______u___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\WallPlotter.h +RES: ________________________________u_u__u_u_____________uuu_uuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Midtbot.cpp +RES: _______u_uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\H2ASpindle.h +RES: ________________u_uu__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Midtbot.h +RES: _______________________________u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\PWMSpindle.cpp +RES: _____________________uuu_uuu__uuu__uu_u_u_uu_u_u_uuu__u__u_uuu_uuuu__uuu____uuuu_u__u_______uuuuu__uuu__uu___u_uuu___uu__u_uu______uuu____uuuuuu_uuu_uuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homing.cpp +RES: ________________________________________uuuuu_uuu_u_uuu___u__u_uu_u_u___u_uuuu_uuuuuuu_______u_uuuu_u___uuuuu_u__uuuuuuuuu_uu_uuuu_uu_u__uu__u_uu__u__u_uu__u_____u_u_u_u_uuu_u__u_u__u_uu_uuu___uuu_u__uuuuu__uu_u__uu___uuu_u__uuuuu___uu_u_uu___________u________uu__uuu_uuuu_uuu_uu_uuuuu_uuuu_u_uuuuuu__u___uu_uuuuuu__uuuu_uuu_uu__u__uuu_u_u__u___________uuu_uuuu__u_uuuuuuuu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.h +RES: __________________________________u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Commands.cpp +RES: __________________________uuuu_uuu_uuuu___uu___uuuu_uuu____u____uu_uuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\RcServo.cpp +RES: _________________________________uuuuu__u_uu_uu_u_uu_uuu_u_uu__uuu__u_uu_uuu_u__u_uu_uuu_u_uuu_uu_u_uuu_____u_uu__u____uu_uu_uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\PwmPin.cpp +RES: _____________u_____uu_____________uu___uuu_uu_uu__uu_uu__u__uuu_u__uuuuu_u__u_____uu__uu_uu_u_uu____uuu_uu_uuuuu__uuu_uu_uu__uuuu_uu__uu__uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\PwmPin.h +RES: _________________u_____________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Arduino.cpp +RES: ________uuu_uu_uuuu__u_ccc_ccc_uuuu_cccc_uuuu_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.h +RES: ________________________________uuu__u__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SoftwareGPIO.h +RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_uu_u_cc_c_uu___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cuuuu__c_cc_cuuuuuu_uc_c_u_ccc_cccc_ccccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\PinOptionParsing.cpp +RES: _______puu__uuu___uuu_uuuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuuuuu_uuuuuu_uu___uuuuuuuu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\TestFactory.cpp +RES: ______________________________________uu__uu____uu__uu_uuuu_u_uuu__u_u_uuu_u_uu_uu_uuuu_uu_uu_uu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\Undefined.cpp +RES: ______p__uu__uuu___uuu__uu_uuuu_p_uu_uu__uu_uu__uu_uu__uu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\ErrorPinTest.cpp +RES: ______c__c_cc_c_cc_pu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Extender\I2CExtenderTests.cpp +RES: ________________u_____u_c_c_____c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__c_cccccc__ccc__cccccc_c_cccccc__ccc__ccccc_cccccccccccccc_____c_cccccccc__c_cccccc__ccc_c__ccccc___________________c_cc__c_cc_uuuu___uuuu_uuuu____uuuu_uuuu__uuuu_uuuu__uu____uuuu_uuuu________u_uu__u_uu_uuuu______u_uu__u_uu_uuuu___uuuu_uuuu___uuuu_uuuuu_cc__cccccc__ccc_c__ccccc___________________c_ccc__cc_cccc___cc_cc_cc____cccc_ccccc__cccc_ccccc__cc__c___cccc_ccc_cc________c_cc__c_cc_ccc_cc______c_cc__c_cc_ccc_cc___ccc____ccc____cccccccc___ccc____ccc_c_c__ccccccccccc_cucccccu_cu_c_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c__cc_c__c_cc_ccccc_c___ccc____cc__ccc_ccuuu__u__uuuuu_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c___ccc_c____cc____cc__c__cc_c__c__cc__cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.h +RES: ______________c____c__c_____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-port.h +RES: ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________c___________cccc_c_c__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlTreeBuilder.cpp +RES: ________________uccccc_______ucccc__________cc_______uccccc________uccccc____ccc_c____c__cc____cc_cuuu_cc___cc_cuuu_cc__cc_cuu_cc__cc_cuu_cc_________cc__ccuu___uuu_uu_cc_________cc__ccuu___uuu_uu_cc_________cc__ccuu___uuu_uu__c_ccccu__uuuuu_uuuuu_uuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\NutsBolts.h +RES: ____________________________________________________________________________________________________________uuu____uuu_uuu_____uu___uu____uuu__uuu_uu_uu__uuuu___uuuuu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\ParserHandler.h +RES: _______________________cc___c______cc_______c__c____cc_c___cc__u__uu_cc_______c_________c____cc_c__c_ccuu_c_ccu_c_uuu_u_uuuu_u_uuu_u_uuu_u_ccc_c_uuuuuu_uuu_u_c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axes.cpp +RES: _________________uuuuu_uu_uuu__uuuu__u__uuuuu_u_uu_uuuuu_uu_uuuu_uu___uuu_uuuuuuuu_u__u_uu_uuu_uu_____uu_uu_uuuu_uuu___uuu_uuuu__uuuu___uuu__uuuuuuuu_uu_uu_uuuuu___uuuuu_uu_uuu_uuuuuuu_u_u_uuu___uuu____u____uuuu_uuu_u_uuuu_u_uu__uuu_uu_uuuu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StackTrace\AssertionFailed.h +RES: _____________________________c__u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlParser.cpp +RES: ______cc____cc_cc__c_cc__c_cc_ccc_cc_____cc_cc__c_c_ccc___c_cc_ccc_cc_______cc_cc__c_cc_cc___c_cc_cc___c_cc_ccc_cc__________cc_cc__c_cc__cc_cc_c_cc___c_cc_cc_c_cc____c_cc_ccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\H2ASpindle.cpp +RES: ____________________u_uuu_uuuuuu_u__uu_uuuuuu_uuuu__uuuuu___uu_uu__uu_u_uuuu_u_uuu__uuuuu___uuuuu_uuu__uuuuu_____uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\gtest.h +RES: __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________c_______c____________________________________________________________________________u__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlComplete.cpp +RES: ______cc_____________________________________cc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_cc_c_cccccuuuuuuuuu_uu_u_u_uuuuu_uu_u_uuu_uuu_uuu_uuuuuu__uu_u_uuu_uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Tokenizer.h +RES: ________________ccc_cc_cccc_c_ccu_ccc_c_c_cccc_c_cccuuu_cccc__________c_____________c_____c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\NotificationsService.h +RES: ____________u__________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.cpp +RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccu_u_u_u________c__cc______cu__c_u__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccu_uuu_ccc_c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.h +RES: ________________________________u_______uu_______________u_uu_uuu_u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\GCode.cpp +RES: ________________________uuuu_____________u_u__uuuu___uuu_u_uuuuuuuuu_u___u_u__u_uuu_u_u_uuu__u__uu___uu_______u__u_uu__u_u_u_uu_uuu_uu_______u_u__________uuu_u_uu_uuuu____u_uuuuu_________u___uuuuuu_u_uuu_uuu________uu___u____u__uuuu_u_uu__uu_uu_u___u_uu_uu_uu_uu___uu_uuu_uuu___uuuu_uuuu_uuuu_uuuu__uuu___uu_uu_uu_uu_uu_uu_u__uuu__uuu_uuu_uuu_uuu_u_uuu_u__u_u__u_u_uuu_u__uu_u__u_uuu_uuu_uuu_uuu____uu_____uu__uuuuuuu_uuu_uuu_uuu_uuu_uuu_uuu_uuu__uu__uu_u_uu___uuu_uu__uu_u__uuu__uu__uuu__uuu___u_uu_uuuu_u_u__uu_u_uu___u_uu_u_uu_u_uu___uu_uuuuu_u_uuu_uuu_uuu_uuu_uuu_uuu_u___uuu_uu______u_uuuuuu_u_uuuuuu_u_uuuuuu_u__uu_u_uuu__uuuu_uuuu_uuuu_uuu_uuu_uuu_uu_u_uuu_uuu_uuu_uuu_uuuu_uu_u_uuuuuu_u_uuuuuu_u_u__uuu___u_uu__u_u____________________________uuu___u_uu_____________uuu_uu_uu_uuuu______________u_uuuu_uu______uu_________uuuuu_u____uuu_u__uuu_u_uuu_uu__u_uuuu_uuuu_uuu____uuuu_u___________uuuu_________uu__uu_uu__________u____uu_uu_uuuu_uu___uu_uu_u_uu_uu__u_uu__uuu_u_u__uu__uu___uu_uuu_uu_uu_____uuuuuu__u_uuuu_uu___u___u____uuuu_u_uuu_uuu_u____u__u___uu_______u__uuu__uu__uu___u_uu_u_u_u___uu_u_u_______uu_uu___uuuuuu__uu__________________________________________________uuu__u_uu____________________uuu__uuuuu_u_uuuu_u__uuu_u_uuuu_uu___u__u__uu_____uu_uu________u_uuu___uu___uu________uu____u__uu_uu__uuuuuuu__u__uu_uu____uuu__uu_uu_u_uu_______uu___uuu__uu_uuuuuuuuu_u___u__uu____uu__u___uuuu_u_u______uu_u_uu_uu_u__uuuu___u__uuuu_uuu_uu__uuuuuu_uu_uu_uu____uuuu____uu__u_u______uuuu__uuu___uuuu____u__u_u_uuu_u____uuu_uuu_uu_uu_uuu_uu_______uuuuuuuuuuu________u__uu_u____uuuu______uu_u___u_uuuu_u__u_____uuuu_uuuuuuuu____uuuu___uuuuuu_uu__u__uu__________________________u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2SOBus.cpp +RES: ________uuuuu_u_uuuuu_uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\UnipolarMotor.h +RES: ______________u_____uuuuuu_uuuuuuu__u______uuuu_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\UnipolarMotor.cpp +RES: _______uuuuuuuu_uu_u_uuuuuu_uu_u_uu__uu__u_uuuu_______________uu_uu_uuu_uu_uuu_uu_uuu_uu_uu__uu_uuu_uuu_uuu_uu____uuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Channel.cpp +RES: _______uuuuuu_uuu_uu_______uuuu________uuuu_uu_uuuu_u_uu_u_uu______uuuuu_uu____uuuuu_u__u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.cpp +RES: _______cc______ccccc_ccc_ccc__c_c_c_cu___cu___cu___cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Tokenizer.cpp +RES: ___________ccuuc_c_cuuuuu_c_u_c_cc___c___c____c_ccccc_ccccc_c_uu__uuuuu__uuu_u__cc__cu__cccccc__cuu_cu_c__ccc__c_____cccccc_cccccccu_c____cccccccu______c___c_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.h +RES: ________________________c______________________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\ParseException.h +RES: ___________________uuuuuuu_uuuu_uu_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\UserOutputs.cpp +RES: ___________uuuuuu_uuuuuuu_u_u_u_uu_uuuuu_uuuuu_uu_uuuuuu_uuuu_uuu_uu__uu__u_uuuu__uu__u_u_uu_uuuuuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepper.cpp +RES: ___________________________________________________________________uuu_uuu_uu____________________________________________________________________________________________________________________u_uuuu________uu_u__u_u_u_________uu__uuu____u_____uu__________u_uuu_uu_uu_uu__uu____uu_uu___u_u_uuuuuuuu__u_uu_uu__uu__u__u_u__uu_uuuu__u_u_u__uuuuuuuuu_u__uuuuu_u__u_uuuuu__uuuu__u_uuuuuuuuuuu__uu__uuuu______________u_uu__u_u_uuuu__uu___uuuuu_u_u___uu_u____uu____u________u__uuuuu_uuuuu___u_uu_uu_________uuu__u_uu_uuuu_u_uu__uuuuu__uuuuuuu___uu___u_uuuuuu_uuuu_uu_u_uuuu_uu___uu_u___u___u______u_______________uuu__uu_uu___u_uuuu_uuuuuu_u__uuu_uuuuuu_uuu_u____uu_uuuuu_u__uu_uuuuu___uuu__uuuuu__uuuu__u____uuu_uu______uuuu_u_uu___________uuuu__uu__uuu_u____________u_u____u___uuu_uuuu__u__uuu__uuu_u_u___uuu_uu_uuu_uu__uu_____uu_____u_u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Validator.cpp +RES: _____________u_uu_uu_uu__u__uu_u_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Validator.h +RES: _____________________uu____uuuuuuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Error.cpp +RES: _______c____________________________________________________________________c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.h +RES: ________________________________________________________________________________________c____c___cc___c___c__ccccccc__________________c________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.cpp +RES: _____________________c___cuuuc_cc__uuuuu_uuu__u_u__uuu_u__uuuuuuuuu_uu_uuuuu_uuuu_uuuuu__uuuuuu_uuu_u__uu_u_uuuuu____uu_uuu_uuuuuuu_u__u_uuu_uuuuuu_u__uuuuuuuu_u_uuuuuu__uuuuu_uuuu_uuuu_uu_____uuuuuuuuu_c_uuuuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.cpp +RES: _____________________________________uuuuu_uuuuu_uuu_u_uuuu_____u_uu__uu__uuu_u_uuuu_u_uu__uuu_u__uuu_uuu_u_uuuu__uuu__uuu_uu_uu_uu__uu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.cpp +RES: ___________u_u__u_uu_uuu__uu__uuu__uuuuuuuuu____u_u_uuuuuuuu__u_uuuuuuu__u_uuuuuu_u__u_uuuuuuu__u_uuuuuuuu_u_uuuuuuuuu_u_uuuu_u_uuuuu___u_uuuuuuuuuu_uu____uuu_uuuuuuuu_uuuuuuuuu_u_uuuuuuuuu_uu_u_uuuuuuu____u_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.h +RES: __________________uuuuuuu_u___u________u_____u_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\NutsBolts.cpp +RES: _______________________uu__u_uuuuuu___uuuuuuuuuuu_uuuu_uuuuu_uu_uu____u__uuuuuuuu_uu___uuuu_uuu_uuu_uuu__uuuuuu_uuu__uu_uuuu__uuu_uuuuuu_uuuuuuuu___uuuuuuu_u____uu_uuuuuuu_uuu_uuu_u__uuuuu__uuuu_uuu_uuu_uuuu_uuu_uuu_uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\InputFile.h +RES: _____________________________________________________u___u______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\HuanyangSpindle.cpp +RES: _______________________________________________________________________________________________________________________________________________u__u_u_uu__uu_u_uu_uu_u__u_u____uu______________________uu__uuuuu__u_uu__uu_uu_u_u_uu__uuuuu_u_uu__uu_u____uuu_u_uu__uu_u_______u_uuu_uu_uuu_u__u_u_u_uuuu_uu_u_uu_uuuuu_u_uu_uuuu______uu_u_______uu_u_uuu_uuu_u_uu__uuuuu_uuuu_uu_u_uu__uuuuu_uu__uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\HuanyangSpindle.h +RES: ______________uuuu__________u__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.cpp +RES: _________ccuuu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.cpp +RES: _________________uuu__u_uuuu__u_u_uu_u_uu_uuu_uuuu_u_u___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.h +RES: _____________________________________u_u________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\CoreXY.cpp +RES: _____________________________u_u__uuuuu_uu__u_uuuuuuuuuu___uu__u__uuuuuu_u___u_u__uuuuuuuuuu_u_uuuu______u_uu__u_____uu_uu_u_uuu_u_u__u_uu_uuu_u_uuu__uuu__uuu_u_uu____uuu_uuuu_u__uu__uu_uu__uuuu_uuuuuu_uuuuuuu__u_u_u__uu_uuu_u__u__uuu_u_uu_uuu__u_uu_uu_u_u___________u_____uuuu_u__u_u_uu__u__uu_______u__uu_uuuu____uuu_uuuuu___uu_u___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\CoreXY.h +RES: ____________________________________u_u__u_u___________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepping.cpp +RES: __________ccccc_uu_____uu__uu_____uu_uuu_uuuuu_uuuu__uu_u_u_uuu_u__uu__u_uu_uuu_u__u__u_u_uuuuuu_u__uuu_u___uu___uuu_u_uuuuuu_u_uuuuu_u_____________u___u_u______u_uu_uuuuuuuu_uuuuuu__u_uu__u_u__u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\xtensa\core-macros.h +RES: ____uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\OnOffSpindle.cpp +RES: _______uuuu__uuu_u_u___u_uuu__uuu_uuu____uuuu_u__u_uuuu_u_u_uuu__uu_u_uuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Kinematics.cpp +RES: __________uuuu_uuuu_uuuu_uuuu_uuuu_uuu_u_uuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\InputFile.cpp +RES: _________u______uuu_uuu_uu_uu_uuuuu__uuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Servo.cpp +RES: __________________________uuuu_uuu__uu________u_u_uu_uuuu_uuu_u_____uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Servo.h +RES: ________________u__________u_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ProcessSettings.cpp +RES: _________________________________uuu_u_u_uu_u_u_u___uu_uu_uu_uu_uu_uu_u__uuu____uu_u_uuuuu__uuu_uuuu_u_uuu_uu_uuu_uuuu_uu_uuu__uuuuuuuuu__uu_uuuuuu_uu__uuuuu________uuuuu_uuuu_uuuu_uuu__u_uuuuuuuuuuu_uuuuuuuu_uuuuuuuuu__uuuuuuuuuuuu_uuu_uuuuuuu_____uuuuuuu_uu_uuu_uuu_uuuu_uuuuu_uuuu_uu__uuuuuuuu_uuuuuuu_u__uu_uu__uu__u_u_u_u_uuuuu__uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_uuuuuuuu_uuuuuuuuuuuuuuu_uuuuuuuu_c____cuuu_uuu_uuu_u_uuu_uuu_uuu______uu__uuuu_uuuu_uuuuu_uuuuuu_uuuuuuu___uuuuu_uuuu_uuuuuuu_uuuuuuu___uuuuu_uuu__uuuuuuu__u_uuu__uu_uuu_uuu_uuu__uu_uuuuuuuuuu_uuu_uuu__uu_uuuuuuuuuu_uu_u_uu_uuu_uuuuuuu_uu_uuu_uu_uuuu_uuuu______uuuuuuuu_uuuuuuuuuuuuuuuu_uuuuuu_uuuu_u_uu______u_____uuu__uu_____u___u_uu___u________uuu_uu_uuuuuuuu_uuuuu_uuu_uuu___uuuu_uuuuu__u___uuuu_uuuuu__u___uuuu_u_u_____uuuuuuuu_uuuu_uuu_uuu_uu__uuu_u__u_uu_u_uu_u___u______uu_u_uuuu___uuuu_uu_uu_uu__uu__uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.h +RES: _____________________uu____uuuuuuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axis.cpp +RES: _______uuuuuuu__uu_uuuuuu_uuuuu_uuuuuu_uuuu____uuu_u_uuuuuuu__uuuuu_uuu__u__uuuuuu_uuu___u_uuuu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\FileStream.h +RES: ___________________u_______uuuuu_______u_________u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Motor.cpp +RES: ____________uuuuuuuuuuu_uuu_u_uuu_u_uuuu_uuu_u__u__uuuuu_u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.h +RES: _________u_u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\JsonGenerator.cpp +RES: ____________uu_uuuu_uuuuuuuuuuu_uuu_u_uuuuu_uuuuu_uuuuu_uuuu_uuuu_uu_uuuuu_uu_uuuu_uuuu_uu_u_uuuuuu_u_________u_uuuuuu_uuu_uu_uu_u_uuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\JsonGenerator.h +RES: _______________________________uu_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Settings.cpp +RES: _____________uuuuuuuuuuuu__u_____uuuuu_____uuuu___uuuu_uuuuuuu_u_uuu_uu_uu___uuuu__u___________uuuu_uuuuuuu_u_uuuuuuu__u_uuuuu__uuu_uu___uu__uuuuuu_u__uuu_u_uuu_u___uuuuu_uu__uuu_uuuu_u__________uuuuuu_uuuuuuu___uuuuuuuu_uuu_uuuu_u_uuuu_uuu_uuuuuuuu_u__uuu_uuu_uu_u_uuuuu_uuu_uuu___________uu_uuuuuuu_u_uuuu_u_____uuuuu_uu___uuu__u_uuu_uuu_uuu__uuuuuuu_u__uuu_uuuuuuuuuuuuuuu_uuuuuuu_uuu_uuuuuuuuuu_uuu_uu__u_u_u_______u___u_u_uuuu_uu________u_u________uuuuuuu_u_uuuuuuu_u_uuuu_u_uuuuu_uuu_uuuuuuu_u__uuu_uuuuuuuuuu_uuuu_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homing.h +RES: _________________________________uuuuuuuuu__u_uuuuuuuuuuu_u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Motor.h +RES: ___________________________u_uu____u____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ControlPin.cpp +RES: _______uuuuu_u_uuu_uuuu_uuuu_uuu_u_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\FileStream.cpp +RES: _________uuuu_uuuuuuu_uuu_uuu_uuu_u_uu_u_uu_u_uuuu_uu_u__uuu_uuuuuuu__u__uu___u__uuuu_u_uu_uuuu_u_u_uuuu_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axis.h +RES: ____________________uuuuu____u_uuuuu_________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\EnumItem.h +RES: _____________c__c____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SettingsDefinitions.cpp +RES: ___________c________c_c_uuuuuu_u_uu___uuuuuuuu_u_u__u_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\PWMSpindle.h +RES: __________________________________u_u____________u_uu__u_u________u________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\OnOffSpindle.h +RES: ____________________uuuuu_uu____________________u_uuuu_u__u______u_u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\System.cpp +RES: ____________________u_uuuuuuuuuu_uuuuuu_u_uuuuuuu_u_uuu_c__________c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.h +RES: ___________________________________________________cu___________________________________u_____________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.cpp +RES: __uuuuuuuu_c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.cpp +RES: _________ccccc_c_uuuuuu_ccc_c_cccu_c_cc_uu_u_u_u_u_u_u_u_u_u_u_uc___cccccc_c___ccc_cccc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.cpp +RES: _______________c_c_cc_____cu_uuuccc__ccc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c___c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_uu_uuu__uuuuuu_u_u__uuu_u_c_c__cc___ccc__cc___c_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.h +RES: ______________________c___________________cc___c___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.h +RES: ______________________________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\Extenders.cpp +RES: ______p_uuu_uuuuu_u_u_uuuuu_u_uu_uu_uuuuuuu_uuuu_uu_uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Configurable.h +RES: _____________________u_u__c__ +PROF: diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index b7dea07c7..4dbc6ee83 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -17,21 +17,34 @@ namespace Extenders { I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} - uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + uint8_t I2CExtender::I2CGetValue(uint8_t address, uint8_t reg) { + auto bus = _i2cBus; + + // First make sure the bus is empty, so we read that which we have written: + // while (bus->read(address, ®, 1) != 0) {} + int err; if ((err = bus->write(address, ®, 1)) != 0) { log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + + IOError(); return 0; } else { uint8_t result = 0; if (bus->read(address, &result, 1) != 1) { log_warn("Cannot read from I2C bus: " << "no response"); + + IOError(); + } else { + errorCount = 0; } return result; } } - void I2CExtender::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + void I2CExtender::I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { + auto bus = _i2cBus; + uint8_t data[2]; data[0] = reg; data[1] = uint8_t(value); @@ -40,16 +53,21 @@ namespace Extenders { if (err) { log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + IOError(); + } else { + errorCount = 0; } } - void I2CExtender::isrTaskLoopDetail() { - std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); - int registersPerDevice = _ports / 8; - int claimedValues = 0; - uint8_t commonStatus = _operation; + void I2CExtender::IOError() { + if (errorCount != 0) { + delay(errorCount * 10); + if (errorCount < 50) { + ++errorCount; + } + } - // Update everything the first operation + // If an I/O error occurred, the best we can do is just reset the whole thing, and get it over with: _status = 1; if (_outputReg != 0xFF) { _status |= 4; // writes @@ -57,10 +75,22 @@ namespace Extenders { if (_inputReg != 0xFF) { _status |= 8; // reads } + } + + void I2CExtender::isrTaskLoopDetail() { + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + int registersPerDevice = _ports / 8; + int claimedValues = 0; + uint8_t commonStatus = _operation; + + // Update everything the first operation + IOError(); // Main loop for I2C handling: while (true) { - uint8_t newStatus = 0; + // If we set it to 0, we don't know if we can use the read data. 0x10 locks the status until we're done + // reading + uint8_t newStatus = 0x10; newStatus = _status.exchange(newStatus); newStatus |= commonStatus; @@ -89,9 +119,10 @@ namespace Extenders { if (_invertReg != 0xFF) { uint8_t currentRegister = _invertReg; uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { uint8_t by = _invert.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); currentRegister++; if (currentRegister == registersPerDevice + _invertReg) { @@ -101,12 +132,12 @@ namespace Extenders { } // Configuration: { - for (int i = 0; i < claimedValues; ++i) { - uint8_t currentRegister = _operationReg; - uint8_t address = _address; + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { uint8_t by = _configuration.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); currentRegister++; if (currentRegister == registersPerDevice + _operationReg) { @@ -138,7 +169,7 @@ namespace Extenders { for (int i = 0; i < claimedValues; ++i) { if ((toWrite & (1 << i)) != 0) { uint8_t by = handleInvertSoftware ? (_output.bytes[i] ^ _invert.bytes[i]) : _output.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); } currentRegister++; @@ -158,7 +189,7 @@ namespace Extenders { for (int i = 0; i < claimedValues; ++i) { auto oldByte = _input.bytes[i]; - auto newByte = I2CGetValue(this->_i2cBus, address, currentRegister); + auto newByte = I2CGetValue(address, currentRegister); if (handleInvertSoftware) { newByte ^= _invert.bytes[i]; } @@ -166,7 +197,7 @@ namespace Extenders { if (oldByte != newByte) { // Handle ISR's: _input.bytes[i] = newByte; - int offset = claimedValues * 8; + int offset = i * 8; for (int j = 0; j < 8; ++j) { auto isr = _isrData[offset + j]; if (isr.defined()) { @@ -181,13 +212,15 @@ namespace Extenders { } currentRegister++; - if (currentRegister == registersPerDevice + _invertReg) { + if (currentRegister == registersPerDevice + _inputReg) { ++address; } } } } + _status &= ~0x10; + vTaskDelay(TaskDelayBetweenIterations); } } @@ -334,7 +367,7 @@ namespace Extenders { _status |= 8; } while (_status != 0) { - vTaskDelay(1); + vTaskDelay(1); // Must be +#include #include #include #include @@ -8,6 +9,26 @@ #include "Capture.h" +#include + +namespace { + struct GPIONative { + // We wire 15 to 20. + static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { + // switch (pin) { + // case 20: + // pins[15].handlePadChange(value); + // break; + // } + } + + inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, true); } + inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } + inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } + inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } + }; +} + namespace Configuration { Test(I2CExtender, I2CBasics) { // Initialize I2C bus @@ -147,13 +168,16 @@ namespace Configuration { Roundtrip() { before = Capture::instance().current(); } ~Roundtrip() { - while (Capture::instance().current() < before + 1) { - delay(10); + for (int i = 0; i < 10; ++i) { + while (Capture::instance().current() < before + 1) { + delay(10); + } + before = Capture::instance().current(); } } }; - Test(I2CExtender, SetupPin) { + Test(I2CExtender, ExtenderNoInterrupt) { // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -191,8 +215,6 @@ namespace Configuration { // high = 1, low = 0 { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -202,8 +224,8 @@ namespace Configuration { i2c.setupPin(0, Pin::Attr::Output); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -219,7 +241,7 @@ namespace Configuration { { Roundtrip rt; } auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv.size() == 1, "Expected single data request / response roundtrip, got %d", int(recv.size())); Assert(recv[0] == 0, "Expected read"); Assert(readPin == true, "Expected 'true' on pin"); } @@ -269,8 +291,6 @@ namespace Configuration { // Setup pin for reading: { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -280,8 +300,8 @@ namespace Configuration { i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -292,8 +312,6 @@ namespace Configuration { // Setup another pin for reading with an invert mask and a PU: { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -303,8 +321,8 @@ namespace Configuration { i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -337,4 +355,428 @@ namespace Configuration { Assert(readPin == false, "Expected 'true' on pin"); } } + + Test(I2CExtender, ExtenderWithInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender with ISR on gpio.15 + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(true); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + // Expected register values (see datasheet): + // + // 4 invert + // 1 = invert, 0 = normal + // + // 6 config + // 1 = input, 0 = output + // + // 2 write + // 1 = high, 0 = low + // + // 0 read + // high = 1, low = 0 + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x01); + + i2c.claim(0); + i2c.setupPin(0, Pin::Attr::Output); + { Roundtrip rt; } + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), + uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Read will NOT trigger an update because we have an ISR to tell us when it changes: + { + bool readPin = i2c.readPin(0); + auto recv = Wire.Receive(); + + Assert(recv.size() == 0, "Expected single data request / response roundtrip, got %d", int(recv.size())); + Assert(readPin == true, "Expected 'true' on pin"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Test write pin: + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'low'. + i2c.writePin(0, false); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 0, "Expected write reg 0 = 0"); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'low'. It's already low, no-op. + i2c.writePin(0, false); + i2c.flushWrites(); + // no-op. + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // NOTE: We ended with setting pin #0 to 'high' = 0x01 + + // Setup pin for reading: + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + + i2c.claim(1); + i2c.setupPin(1, Pin::Attr::Input); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), + uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Setup another pin for reading with an invert mask and a PU: + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x04); + + i2c.claim(2); + i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), + uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(1); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Trigger an ISR, change both pins + { + Wire.Send(0x02); + GPIONative::write(15, true); + GPIONative::write(15, false); + { Roundtrip rt; } + auto recv = Wire.Receive(); + Assert(recv.size() == 1); + Assert(recv[0] == 0); + } + + // Test read pin: + { + bool readPin = i2c.readPin(1); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + } + + void HandleInterrupt(void* data) { ++(*reinterpret_cast(data)); } + + volatile uint16_t currentInput = 0; + void WireResponseHandler(TwoWire* theWire, std::vector& data) { + if (data.size() == 1) { + if (data[0] == 0) { + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(currentInput)); + data.clear(); + } else if (data[0] == 1) { + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(currentInput >> 8)); + data.clear(); + } else if (data[0] >= 2 && data[0] <= 7) { + // ignore until next roundtrip + } else { + Assert(false, "Unknown register"); + } + } else if (data.size() == 2) { + if (data[0] >= 2 && data[0] <= 7) { + data.clear(); + } else { + Assert(false, "Unknown register"); + } + } else { + Assert(false, "Unknown size"); + } + } + + Test(I2CExtender, ISRTriggerWithInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(true); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + uint32_t isrCounter = 0; + + { + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + { Roundtrip rt; } + + // Test read pin: + { + bool readPin = i2c.readPin(9); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Change state, wait till roundtrip + { + Wire.Send(0x00); + Wire.Send(0x02); + + // Trigger ISR pin 'falling' + GPIONative::write(15, true); + GPIONative::write(15, false); + { Roundtrip rt; } + + auto recv = Wire.Receive(); + Assert(recv.size() == 2); + Assert(recv[0] == 0); + Assert(recv[1] == 1); + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin = i2c.readPin(9); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + } + + Test(I2CExtender, ISRTriggerWithoutInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + uint32_t isrCounter = 0; + + { + // From this point on, we just need to respond to wire requests + currentInput = 0x0000; + Wire.Clear(); + Wire.SetResponseHandler(WireResponseHandler); + + i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); + } + + // Test read pin: + { + bool readPin = i2c.readPin(9); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Change state, wait till roundtrip + { + currentInput = 0x0200; + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin = i2c.readPin(9); + Assert(readPin == true, "Expected 'true' on pin"); + + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin2 = i2c.readPin(9); + Assert(readPin2 == true, "Expected 'true' on pin"); + } + + Wire.Clear(); + } } diff --git a/FluidNC/test/Pins/GPIO.cpp b/FluidNC/test/Pins/GPIO.cpp index 39ec3d49d..e9dd09635 100644 --- a/FluidNC/test/Pins/GPIO.cpp +++ b/FluidNC/test/Pins/GPIO.cpp @@ -8,38 +8,41 @@ extern "C" void __pinMode(uint8_t pin, uint8_t mode); extern "C" int __digitalRead(uint8_t pin); extern "C" void __digitalWrite(uint8_t pin, uint8_t val); -struct GPIONative { - inline static void initialize() { - for (int i = 16; i <= 17; ++i) { - __pinMode(i, OUTPUT); - __digitalWrite(i, LOW); +namespace { + struct GPIONative { + inline static void initialize() { + for (int i = 16; i <= 17; ++i) { + __pinMode(i, OUTPUT); + __digitalWrite(i, LOW); + } } - } - inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); } - inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); } - inline static bool read(int pin) { return __digitalRead(pin) != LOW; } -}; + inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); } + inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); } + inline static bool read(int pin) { return __digitalRead(pin) != LOW; } + }; +} #else # include -struct GPIONative { - // We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17: - static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { - switch (pin) { - case 16: - case 17: - pins[16].handlePadChange(value); - pins[17].handlePadChange(value); - break; +namespace { + struct GPIONative { + // We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17: + static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { + switch (pin) { + case 16: + case 17: + pins[16].handlePadChange(value); + pins[17].handlePadChange(value); + break; + } } - } - - inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); } - inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } - inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } - inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } -}; + inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); } + inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } + inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } + inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } + }; +} void digitalWrite(uint8_t pin, uint8_t val); void pinMode(uint8_t pin, uint8_t mode); int digitalRead(uint8_t pin); diff --git a/X86TestSupport/TestSupport/SoftwareGPIO.h b/X86TestSupport/TestSupport/SoftwareGPIO.h index 061578f9b..7332c1c98 100644 --- a/X86TestSupport/TestSupport/SoftwareGPIO.h +++ b/X86TestSupport/TestSupport/SoftwareGPIO.h @@ -128,7 +128,7 @@ class SoftwareGPIO { pins[index].handlePadChangeWithHystesis(value); } } else { - pins[index].driverValue = value; + pins[index].handlePadChange(value); } } diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 69373b035..2ada77bbe 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -1,10 +1,11 @@ #include "Wire.h" +#include + TwoWire Wire(0); TwoWire Wire1(1); -TwoWire::TwoWire(uint8_t bus_num) {} -TwoWire::~TwoWire() {} +TwoWire::TwoWire(uint8_t bus_num) : handler(nullptr) {} // For unit tests: void TwoWire::Send(std::vector data) { @@ -13,17 +14,21 @@ void TwoWire::Send(std::vector data) { } } void TwoWire::Send(uint8_t value) { + std::lock_guard guard(mut); receivedData.push_back(value); } std::vector TwoWire::Receive() { - std::vector data; + std::lock_guard guard(mut); + std::vector data; std::swap(sentData, data); return data; } void TwoWire::Clear() { + std::lock_guard guard(mut); sentData.clear(); receivedData.clear(); + handler = nullptr; } // TwoWire interface: @@ -81,6 +86,8 @@ uint8_t TwoWire::endTransmission(void) { } size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { + std::lock_guard guard(mut); + auto available = receivedData.size(); if (available > size) { available = size; @@ -113,8 +120,15 @@ uint8_t TwoWire::requestFrom(int address, int size) { } size_t TwoWire::write(uint8_t ch) { - Assert(inTransmission, "Should be in a transmission"); - sentData.push_back(ch); + { + Assert(inTransmission, "Should be in a transmission"); + std::lock_guard guard(mut); + sentData.push_back(ch); + } + + if (handler) { + (*handler)(this, sentData); + } return 0; } @@ -125,9 +139,11 @@ size_t TwoWire::write(const uint8_t* buf, size_t size) { return size; } int TwoWire::available(void) { + std::lock_guard guard(mut); return receivedData.size(); } int TwoWire::read(void) { + std::lock_guard guard(mut); if (receivedData.size()) { auto result = receivedData[0]; receivedData.erase(receivedData.begin()); @@ -137,6 +153,7 @@ int TwoWire::read(void) { } } int TwoWire::peek(void) { + std::lock_guard guard(mut); if (receivedData.size()) { auto result = receivedData[0]; return result; @@ -146,5 +163,7 @@ int TwoWire::peek(void) { } void TwoWire::flush(void) {} +TwoWire::~TwoWire() {} + extern TwoWire Wire; extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index 1bda1ea24..b28f807ca 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -2,6 +2,8 @@ #include #include +#include + #include "esp32-hal-i2c.h" #include "Stream.h" #include "../FluidNC/test/TestFramework.h" @@ -10,6 +12,10 @@ class TwoWire : public Stream { bool inTransmission = false; std::vector receivedData; std::vector sentData; + std::mutex mut; + + using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data); + ResponseHandler handler; public: TwoWire(uint8_t bus_num); @@ -18,8 +24,11 @@ class TwoWire : public Stream { // For unit tests: void Send(std::vector data); void Send(uint8_t value); + size_t SendSize() { return receivedData.size(); } std::vector Receive(); + size_t ReceiveSize() { return sentData.size(); } void Clear(); + void SetResponseHandler(ResponseHandler handler) { this->handler = handler; } // TwoWire interface: From 08c47070a3c5cd14711ff4f0e31f8bfcf9f54998 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 17:35:00 +0100 Subject: [PATCH 007/100] Small fixes --- FluidNC/src/Extenders/I2CExtender.cpp | 1 + FluidNC/src/Main.cpp | 88 --------------------------- 2 files changed, 1 insertion(+), 88 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 4dbc6ee83..9a378b36b 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -219,6 +219,7 @@ namespace Extenders { } } + // Remove the busy flag, keep the rest. _status &= ~0x10; vTaskDelay(TaskDelayBetweenIterations); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 06e92cd44..d5ec53066 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -28,51 +28,6 @@ extern void make_user_commands(); -// FOR TESTING: - -# include -# include - -extern "C" void __pinMode(pinnum_t pin, uint8_t mode); - -uint8_t I2CGetValue(uint8_t address, uint8_t reg) { - Wire.beginTransmission(address); - Wire.write(reg); - auto err = Wire.endTransmission(); // i2c_err_t - - if (Wire.requestFrom((int)address, 1) != 1) { - Uart0.println("Error reading from i2c bus."); - return 0; - } - uint8_t result = Wire.read(); - return result; -} - -void I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { - uint8_t data[2]; - data[0] = reg; - data[1] = uint8_t(value); - - Wire.beginTransmission(address); - for (size_t i = 0; i < 2; ++i) { - Wire.write(data[i]); - } - auto err = Wire.endTransmission(); // i2c_err_t ?? - - if (err) { - Uart0.println("Error writing to i2c bus; PCA9539 failed. Code: "); - Uart0.println(int(err)); - } -} - -volatile bool fired = false; - -void isrHandler() { - fired = true; -} - -// --- Until here. - void setup() { try { uartInit(); // Setup serial port @@ -83,49 +38,6 @@ void setup() { WebUI::WiFiConfig::reset(); - /* TEST STUFF! */ - - /* - // THIS WORKS: - { - Uart0.println("Basic test of pin extender."); - // Wire.begin(sda , scl, frequency); - Wire.begin(13, 14, 100000); - - Uart0.println("Setup pins:"); - - // 1. Setup pins: - I2CSetValue(0x74, 6, 0xFF); // All input pins - I2CSetValue(0x74, 7, 0xFF); // All input pins - - __pinMode(36, INPUT); - attachInterrupt(36, isrHandler, CHANGE); - - // 2. Read input register: - Uart0.println("Main loop:"); - while (true) { - auto r1 = I2CGetValue(0x74, 0); - auto r2 = I2CGetValue(0x74, 1); - uint16_t v = (uint16_t(r1) << 8) | uint16_t(r2); - Uart0.print("Status: "); - for (int i = 0; i < 16; ++i) { - uint16_t mask = uint16_t(1 << i); - Uart0.print((v & mask) ? '1' : '0'); - } - - if (fired) { - Uart0.print(" ** ISR"); - fired = false; - } - - Uart0.println(); - - delay(1000); - } - } - */ - /* END TEST STUFF */ - display_init(); // Load settings from non-volatile storage From 56b28a467ff2523176fa9ed1131ef8741c208f88 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Mar 2022 14:03:35 +0100 Subject: [PATCH 008/100] Fixed unit tests and a few more small bugs. --- CodeCoverage.cov | 54 +- FluidNC/src/Extenders/I2CExtender.cpp | 7 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 554 +++++++++++---------- X86TestSupport/TestSupport/Wire.cpp | 5 +- X86TestSupport/TestSupport/Wire.h | 8 +- 5 files changed, 331 insertions(+), 297 deletions(-) diff --git a/CodeCoverage.cov b/CodeCoverage.cov index a3f4cc636..8c6613e6f 100644 --- a/CodeCoverage.cov +++ b/CodeCoverage.cov @@ -14,13 +14,13 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Stand RES: _______________________u_uuuu_uu_uuuu_u_uu_uu_u_uuuuuuuuuuu_uuuu_uuu__uuuuuuuuuuu__uu_uuu_uuuuuu_u_uuu_u_u_u___c__uuuuuuu__uuuu___u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.h -RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________ccccuuuu__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ +RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________cccccccc__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Authentication.cpp RES: ___________________________________________u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.cpp -RES: ____cc_c__ccccccccccccccc_cccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_u___ +RES: ____cc_c__ccccccccccccccc_ccccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_c___ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.cpp RES: _______uuuu_ @@ -35,25 +35,25 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSuppo RES: ____cuuuuuuuuu_c__c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.h -RES: ___________c______________c_c_c_________________________________________________ +RES: ___________c_______________c___cccc_________________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.cpp RES: ______________________________u_u_uuu_uuu__u_u_uuu_uuu___u_uuuu_uu_uu_uuu_____uuuuuu__uuu___uuuuuuuu_uuu_________________________________________________________________________________________uuu___uuu__u_uu___u_uuuu_uuu_uu_uu__uuu___uuuu_u_u_uu___u_uuuuuuuu__uuu_uu_uuuu_u______uuuuuu_uuuuu_____uuu_uuuuu_uuuuu_uuuuuuuuu_uuuuuuuuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPIFFS.cpp -RES: ____cu___uuuuuuu__c +RES: ____cc___uuuuuuu__c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.cpp RES: ____________c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.cpp -RES: ________c_u____________cccuucc___u_u_ +RES: ________c_c____________cccuucc___u_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.cpp RES: ______________________________________ccccccc_u_u__uuuuuuu_uuuuu_u_uuuu_uu_uuu_ccc_uuu_uuu_ccc_ccc_cccuu_cc_ccuuc_c_uuuuu_uu_uuuuu_u_uuu_uuu_uuuu__uuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___c_c_c__cu___cc_cc_cc_u_u_u__uu___uuu_uu_uu_uu_uu_uu_uu_uu___uuu___uuuu_u__uuu__uu___uuuuuu_uu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.h -RES: ___________________________________________________________________________________c______c______c________u__cccc__uu_cc___cccc____c_c_u_uu_c____c_c__u_c___u___ +RES: ___________________________________________________________________________________c______c______c________u__cccc__cc_cc___cccc____c_c_u_cc_c____c_c__c_c___u___ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\rmt.cpp RES: ____uuu_u_uuu_uu @@ -86,7 +86,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuratio RES: ________u_u_uuu_uu_uuuu_uu_uuu_uuu_u_uuuuuu__u_uu_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Task.cpp -RES: _____________c_______ccccc_cccp_uuu_uuuuu_uuu_uuu_uuu_uuu +RES: _____________c_______ccccc_cccc_uuu_uuuuu_uuu_uuu_uuu_uuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Capture.h RES: _______________________c__cccc___uuuuuuuu__________________________ccuuu_u_c_____uuuu_____________uuuuuu_u_uu_uuuuu_u_ @@ -107,10 +107,10 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Limi RES: _____________u_____uu___u_________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.cpp -RES: __________c_ccu_uuuu_u_uuu___uu_u__uuu_uuuuu_u_uuu_u_c_c_u_u_uu_u_uu_uuuu_c_cuucuuuu_ccc_ +RES: __________c_ccc_cccc_c_ccc___cc_c__ccc_ccccc_c_ccc_c_c_c_c_c_cc_c_cc_cccc_c_cccccccc_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\GPIO.cpp -RES: _____________________________uu__uu__u_u__u_________pu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu_uu_uu_uu_uuuuuuu______________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu_p_p_p_pu_uu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uu_uuuuu_u_uuuu_u_uuuuu_pu_uuu_pu_uuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu______uu_uu_uu_uu_____________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu__p_p_p_ +RES: _____________________________cc__cc__c_c__c_________cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_ccccccc______________cccccc_ccc__ccccc_ccc_c__c_ccccc_cc_c_c_c_cc_cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_cc_ccccc_c_cccc_c_ccccc_cc_ccc_cc_ccc_cc_cc_cc_cuuu_u_uuuu_u_uuuuu______cc_cc_cc_cc_____________cccccc_cuc__ccccc_cuu_u__u_uuuuu_uu__c_c_c_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\LedcPin.cpp RES: ______________________u________uuuu_uu_u_u_u____uuuuuu_uuu____uuuu @@ -122,7 +122,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10v RES: ___________________________________u_uuuuu__u_u_________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-internal.h -RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________p____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________c____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StepStick.cpp RES: __________u_uuu_uu__u_uu_uuuuu_uuu__uu_u__u___c__ @@ -149,7 +149,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spi RES: ________________u_uuuu_uuuuu_u_uuuuu_u__uu_uuu_uuuu_________uuu_uuuu__uuu__uuuuu_uuuu_u_uuuuu_uuuuuuu_uu_uuu_uuuu_uu_u_uuu_uu_______uu___uuuuu___u_u_uu_u__u___u_u__uu_uuuu_u_uu___u_u__uu_uuu_uuuu____uu_uu_uuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.cpp -RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__cuuuuuu_uuuuuuuuu_uu_uc__cc_u_c_uuu_uuuuuuuu_c_____c__c___c__c_ccuu___cucu___cu__cc_cccc_cccc_cccu_cu_cu__cc_ +RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__ccuuuuu_cuuuuuccu_uu_cc__cc_u_c_ccu_cccccccc_c_____c__c___c__c_cccc___cucu___cc__cc_cccc_cccc_cccu_cu_cu__cc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.cpp RES: ____________uu_uu__u__uu_u_uu_ @@ -167,7 +167,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Motor RES: ____________________________uuu_u_u_uuuuuuuuuuuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.h -RES: ______________________________________u____u__uu__uc____________cc__ +RES: ______________________________________u____c__cc__cc____________cc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.cpp RES: _____________u_uuu_uu_uuuuu____uu__u_uu_uuuu_u_u_____________uu_u_uu__uuuu_uuuuu_uuu_u_uu_uu @@ -347,13 +347,13 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinD RES: _______________u___________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Main.cpp -RES: _____________________________________uuuu_uuu_uuu_u_uu_uuuuu_uuu_u___uuu___uuu___u_u____________________________________________u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ +RES: ______________________________uuu___u_u_u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.cpp -RES: _______cu_u_uu_uuuu_u__ +RES: _______cu_c_cc_ccuu_c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.h -RES: ________________________u__ +RES: ________________________c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Limits.cpp RES: __________________u________________u_____uuu_uuuuu_uu____uuu_uuuuuu_uu__uu____uu_uu_uuuuuuu_uu___uu_uuuu_u_uuuu_u_______________________uuuuu__uu_uuuuu__uu @@ -401,7 +401,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homi RES: ________________________________________uuuuu_uuu_u_uuu___u__u_uu_u_u___u_uuuu_uuuuuuu_______u_uuuu_u___uuuuu_u__uuuuuuuuu_uu_uuuu_uu_u__uu__u_uu__u__u_uu__u_____u_u_u_u_uuu_u__u_u__u_uu_uuu___uuu_u__uuuuu__uu_u__uu___uuu_u__uuuuu___uu_u_uu___________u________uu__uuu_uuuu_uuu_uu_uuuuu_uuuu_u_uuuuuu__u___uu_uuuuuu__uuuu_uuu_uu__u__uuu_u_u__u___________uuu_uuuu__u_uuuuuuuu_uuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.h -RES: __________________________________u____ +RES: __________________________________c____ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Commands.cpp RES: __________________________uuuu_uuu_uuuu___uu___uuuu_uuu____u____uu_uuu_u_ @@ -416,28 +416,28 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\Pwm RES: _________________u_____________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Arduino.cpp -RES: ________uuu_uu_uuuu__u_ccc_ccc_uuuu_cccc_uuuu_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ +RES: ________uuu_uu_uuuu__u_ccc_ccc_cccc_cccc_cccc_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.h RES: ________________________________uuu__u__u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SoftwareGPIO.h -RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_uu_u_cc_c_uu___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cuuuu__c_cc_cuuuuuu_uc_c_u_ccc_cccc_ccccc_ +RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_cc_c_cc_c_cc___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cccuu__c_cc_cccuuuu_cc_c_c_ccc_cccc_ccccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\PinOptionParsing.cpp -RES: _______puu__uuu___uuu_uuuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuuuuu_uuuuuu_uu___uuuuuuuu_uuu_ +RES: _______ccc__ccc___cuu_cuuc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_ccc_cc___ccccuuuu_ccc_cc_c_c__cccc_ccc_cc___ccccuuuu_ccc_cc_c_c__ccccccc_cccccc_cc___ccccuuuu_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\TestFactory.cpp RES: ______________________________________uu__uu____uu__uu_uuuu_u_uuu__u_u_uuu_u_uu_uu_uuuu_uu_uu_uu_uu__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\Undefined.cpp -RES: ______p__uu__uuu___uuu__uu_uuuu_p_uu_uu__uu_uu__uu_uu__uu_uuu_ +RES: ______c__cc__ccc___ccc__pc_cccc_c_cc_cc__cc_cc__cc_cc__cc_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\ErrorPinTest.cpp -RES: ______c__c_cc_c_cc_pu_uu_ +RES: ______c__c_cc_c_cc_pc_cc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Extender\I2CExtenderTests.cpp -RES: ________________u_____u_c_c_____c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__c_cccccc__ccc__cccccc_c_cccccc__ccc__ccccc_cccccccccccccc_____c_cccccccc__c_cccccc__ccc_c__ccccc___________________c_cc__c_cc_uuuu___uuuu_uuuu____uuuu_uuuu__uuuu_uuuu__uu____uuuu_uuuu________u_uu__u_uu_uuuu______u_uu__u_uu_uuuu___uuuu_uuuu___uuuu_uuuuu_cc__cccccc__ccc_c__ccccc___________________c_ccc__cc_cccc___cc_cc_cc____cccc_ccccc__cccc_ccccc__cc__c___cccc_ccc_cc________c_cc__c_cc_ccc_cc______c_cc__c_cc_ccc_cc___ccc____ccc____cccccccc___ccc____ccc_c_c__ccccccccccc_cucccccu_cu_c_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c__cc_c__c_cc_ccccc_c___ccc____cc__ccc_ccuuu__u__uuuuu_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c___ccc_c____cc____cc__c__cc_c__c__cc__cc_ +RES: _________________u_____u_c_c__________cc___ccc__cc_cc__ccc_ccc__cc_cc_u__c_ccccccc__cccc_cuccccccu_cu_c__cccccccc_cc_c_ccc_ccc_c_cc_ccc__cc___c_ccc_c_cu_c_c_cccccc______c_cccccccc__c___c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__cc_c__cccccc__ccc_cc__cccccc_ccc__cccccc__ccc_cc__ccccc_cccccccccccccc_ccc__cccccc__ccc_cc__ccccc____cc__c__cc____cccc_____ccccc___ccccc___cc_cc___cccc_ccc_____cc__c_ccc____cc__c_cccc____cc_cc____cc_cc__cc___cc_cc____cc_cc_c_cccc__cccccc__ccc_cc__ccccc__ccc_c____ccc_____cccc___cccc___cc__c___cccc______cc__cc____cc__cc____cccc____cccc____cccc____ccc____ccc_c_c_cccc__cccccc__ccc_cc__ccccc__cc__c_c__c__c__c_c__c___ccc____cc__cc__ccc___c__c_c____cc__cc_c_cccc__cccccc__ccc_cc__ccccc__cc__c_c__c___c____cc____c_c__c__cc___c_c__c__cc___c__c_c____cc__c_c_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.h RES: ______________c____c__c_____________ @@ -479,7 +479,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Notifi RES: ____________u__________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.cpp -RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccu_u_u_u________c__cc______cu__c_u__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccu_uuu_ccc_c +RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccc_u_c_c________c__cc______cu__c_c__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccc_ccc_ccc_c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.h RES: ________________________________u_______uu_______________u_uu_uuu_u__u_u__ @@ -533,7 +533,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.cpp RES: _____________________c___cuuuc_cc__uuuuu_uuu__u_u__uuu_u__uuuuuuuuu_uu_uuuuu_uuuu_uuuuu__uuuuuu_uuu_u__uu_u_uuuuu____uu_uuu_uuuuuuu_u__u_uuu_uuuuuu_u__uuuuuuuu_u_uuuuuu__uuuuu_uuuu_uuuu_uu_____uuuuuuuuu_c_uuuuu_uuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.cpp -RES: _____________________________________uuuuu_uuuuu_uuu_u_uuuu_____u_uu__uu__uuu_u_uuuu_u_uu__uuu_u__uuu_uuu_u_uuuu__uuu__uuu_uu_uu_uu__uu_uuu_uuu +RES: _____________________________________ccccc_ccccc_cuc_c_cccc_____u_cc__cc__uuu_u_uuuu_u_uu__ccc_c__ccu_ccc_c_ccuu__ccu__ccc_cc_cu_cu__cc_ccu_ccc PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.cpp RES: ___________u_u__u_uu_uuu__uu__uuu__uuuuuuuuu____u_u_uuuuuuuu__u_uuuuuuu__u_uuuuuu_u__u_uuuuuuu__u_uuuuuuuu_u_uuuuuuuuu_u_uuuu_u_uuuuu___u_uuuuuuuuuu_uu____uuu_uuuuuuuu_uuuuuuuuu_u_uuuuuuuuu_uu_u_uuuuuuu____u_uuu_ @@ -554,7 +554,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Hua RES: ______________uuuu__________u__u_____ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.cpp -RES: _________ccuuu_uu__ +RES: _________ccucc_uc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.cpp RES: _________________uuu__u_uuuu__u_u_uu_u_uu_uuu_uuuu_u_u___c__ @@ -647,7 +647,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\System.cpp RES: ____________________u_uuuuuuuuuu_uuuuuu_u_uuuuuuu_u_uuu_c__________c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.h -RES: ___________________________________________________cu___________________________________u_____________________ +RES: ___________________________________________________cc___________________________________u_____________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.cpp RES: __uuuuuuuu_c @@ -656,7 +656,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CB RES: _________ccccc_c_uuuuuu_ccc_c_cccu_c_cc_uu_u_u_u_u_u_u_u_u_u_u_uc___cccccc_c___ccc_cccc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.cpp -RES: _______________c_c_cc_____cu_uuuccc__ccc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c___c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_uu_uuu__uuuuuu_u_u__uuu_u_c_c__cc___ccc__cc___c_u +RES: _______________c_c_cc_____cu_uuuccu__uuc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c____c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_cc_ccc__cccccu_c_c__ccc_u_c_c__cc___ccc__cc___c_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.h RES: ______________________c___________________cc___c___c__ diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 9a378b36b..fa7b2bbf8 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -21,7 +21,7 @@ namespace Extenders { auto bus = _i2cBus; // First make sure the bus is empty, so we read that which we have written: - // while (bus->read(address, ®, 1) != 0) {} + while (bus->read(address, ®, 1) != 0) {} int err; if ((err = bus->write(address, ®, 1)) != 0) { @@ -42,6 +42,7 @@ namespace Extenders { return result; } } + void I2CExtender::I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { auto bus = _i2cBus; @@ -68,7 +69,7 @@ namespace Extenders { } // If an I/O error occurred, the best we can do is just reset the whole thing, and get it over with: - _status = 1; + _status |= 1; if (_outputReg != 0xFF) { _status |= 4; // writes } @@ -98,7 +99,7 @@ namespace Extenders { if (newStatus != 0) { if ((newStatus & 2) != 0) { _status = 0; - break; + return; // Stop running } // Update config: diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp index 80f5cba54..9212e84de 100644 --- a/FluidNC/test/Extender/I2CExtenderTests.cpp +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -10,6 +10,7 @@ #include "Capture.h" #include +#include namespace { struct GPIONative { @@ -27,6 +28,158 @@ namespace { inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } }; + + class PCA9539Emulator { + uint8_t reg_config[2]; + uint8_t reg_invert[2]; + uint8_t reg_input[2]; + uint8_t reg_output[2]; + uint8_t pad_value[2]; // input values + + uint8_t accessedRegisters = 0; + uint8_t previousRegisters = 0; // For debugging purposes. + + int isrPin_; + + void setRegister(int reg, uint8_t value) { + accessedRegisters |= uint8_t(1 << reg); + switch (reg) { + // input + case 2: + reg_output[0] = value; + break; + case 3: + reg_output[1] = value; + break; + // invert + case 4: + reg_invert[0] = value; + reg_input[0] = reg_invert[0] ^ pad_value[0]; + break; + case 5: + reg_invert[1] = value; + reg_input[1] = reg_invert[1] ^ pad_value[1]; + break; + // config + case 6: + reg_config[0] = value; + break; + case 7: + reg_config[1] = value; + break; + default: + Assert(false, "Not supported"); + break; + } + } + + void handler(TwoWire* theWire, std::vector& data) { + if (data.size() == 1) { + if (data[0] == 0 || data[0] == 1) { + accessedRegisters |= uint8_t(1 << data[0]); + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(reg_input[data[0]])); + data.clear(); + + // Clear ISR: + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, true); + } + } else if (data[0] >= 2 && data[0] <= 7) { + // ignore until next roundtrip + } else { + Assert(false, "Unknown register"); + } + } else if (data.size() == 2) { + if (data[0] >= 2 && data[0] <= 7) { + setRegister(data[0], data[1]); + data.clear(); + } else { + Assert(false, "Unknown register"); + } + } else { + Assert(false, "Unknown size"); + } + } + + public: + PCA9539Emulator(int isrPin) : isrPin_(isrPin) { + for (int i = 0; i < 2; ++i) { + reg_config[i] = 0; + reg_invert[i] = 0; + reg_input[i] = 0; + reg_output[i] = 0; + pad_value[i] = 0; + } + + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, true); + } + } + + static void wireResponseHandler(TwoWire* theWire, std::vector& data, void* userData) { + static_cast(userData)->handler(theWire, data); + } + + void setPadValue(int pinId, bool v) { + uint8_t mask = uint8_t(1 << (pinId % 8)); + int idx = pinId / 8; + + if (reg_config[idx] & mask) // input + { + auto oldValue = pad_value[idx] & mask; + auto newValue = v ? mask : uint8_t(0); + + if (oldValue != newValue) { + pad_value[idx] = (pad_value[idx] & ~mask) | newValue; + reg_input[idx] = reg_invert[idx] ^ pad_value[idx]; + + // Trigger ISR on 'falling'. + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, false); + } + } + } + } + + bool getPadValue(int pinId) { + uint8_t mask = uint8_t(1 << (pinId % 8)); + int idx = pinId / 8; + + if ((reg_config[idx] & mask) == 0) { + // This is an output pin, so combine registers: + return ((reg_output[idx] ^ reg_invert[idx]) & mask) != 0; + } else { + // This is an input pin, so use the pad_value + return (pad_value[idx] & mask) != 0; + } + } + + uint8_t registersUsed() { + auto result = accessedRegisters; + previousRegisters = result; + accessedRegisters = 0; + return result; + } + }; + + class Roundtrip { + uint32_t before; + + public: + Roundtrip() { before = Capture::instance().current(); } + + ~Roundtrip() { + for (int i = 0; i < 3; ++i) { + while (Capture::instance().current() < before + 1) { + delay(10); + } + before = Capture::instance().current(); + } + } + }; + + std::mutex single_thread; } namespace Configuration { @@ -104,6 +257,10 @@ namespace Configuration { }; Test(I2CExtender, InitDeinit) { + std::lock_guard guard(single_thread); + + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -117,6 +274,9 @@ namespace Configuration { mconfig._i2c = &bus; config = &mconfig; + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + // Setup the extender Extenders::I2CExtender i2c; FakeInitHandler fakeInit(false); @@ -126,6 +286,9 @@ namespace Configuration { } Test(I2CExtender, ClaimRelease) { + std::lock_guard guard(single_thread); + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -139,6 +302,9 @@ namespace Configuration { mconfig._i2c = &bus; config = &mconfig; + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + // Setup the extender Extenders::I2CExtender i2c; FakeInitHandler fakeInit(false); @@ -161,23 +327,10 @@ namespace Configuration { i2c.free(2); } - class Roundtrip { - uint32_t before; - - public: - Roundtrip() { before = Capture::instance().current(); } - - ~Roundtrip() { - for (int i = 0; i < 10; ++i) { - while (Capture::instance().current() < before + 1) { - delay(10); - } - before = Capture::instance().current(); - } - } - }; - Test(I2CExtender, ExtenderNoInterrupt) { + std::lock_guard guard(single_thread); + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -192,6 +345,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -200,25 +354,8 @@ namespace Configuration { i2c.validate(); i2c.init(); - // Expected register values (see datasheet): - // - // 4 invert - // 1 = invert, 0 = normal - // - // 6 config - // 1 = input, 0 = output - // - // 2 write - // 1 = high, 0 = low - // - // 0 read - // high = 1, low = 0 - { // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x01); i2c.claim(0); i2c.setupPin(0, Pin::Attr::Output); @@ -226,24 +363,17 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), - uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + // Check PCA values: + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(!pca.getPadValue(0)); } // Read will trigger an update, because we don't have an ISR { - Wire.Send(0x01); bool readPin = i2c.readPin(0); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 1, "Expected single data request / response roundtrip, got %d", int(recv.size())); - Assert(recv[0] == 0, "Expected read"); - Assert(readPin == true, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x01, "Expected roundtrip for read / no ISR"); + Assert(readPin == false, "Expected 'true' on pin"); } // Test write pin: @@ -252,28 +382,24 @@ namespace Configuration { i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(pca.getPadValue(0), "Expected pad to be 'true'"); } { // Write to set it 'low'. i2c.writePin(0, false); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 0, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(!pca.getPadValue(0), "Expected pad to be 'false'"); } { // Write to set it 'low'. It's already low, no-op. i2c.writePin(0, false); i2c.flushWrites(); // no-op. + Assert(pca.registersUsed() == 0x00, "Expected roundtrip for write / no ISR"); + Assert(!pca.getPadValue(0), "Expected pad to be 'false'"); } { // Write to set it 'high'. @@ -282,82 +408,83 @@ namespace Configuration { { Roundtrip rt; } auto recv = Wire.Receive(); - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(pca.getPadValue(0), "Expected pad to be 'false'"); } // NOTE: We ended with setting pin #0 to 'high' = 0x01 // Setup pin for reading: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - i2c.claim(1); i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), - uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(pca.getPadValue(0)); + Assert(!pca.getPadValue(1)); } // Setup another pin for reading with an invert mask and a PU: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x04); - i2c.claim(2); i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), - uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(pca.getPadValue(0)); + Assert(!pca.getPadValue(1)); + Assert(!pca.getPadValue(2)); } // Test read pin: { - Wire.Send(0x02); bool readPin = i2c.readPin(1); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); - Assert(recv[0] == 0, "Expected read"); + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); + Assert(readPin == false); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); Assert(readPin == true, "Expected 'true' on pin"); } + pca.setPadValue(1, true); + pca.setPadValue(2, true); + + // Test read pin: + { + bool readPin = i2c.readPin(1); + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); + Assert(readPin == true); + } + // Test read pin: { - Wire.Send(0x02); bool readPin = i2c.readPin(2); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); - Assert(recv[0] == 0, "Expected read"); + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); Assert(readPin == false, "Expected 'true' on pin"); } } Test(I2CExtender, ExtenderWithInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -373,6 +500,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender with ISR on gpio.15 Extenders::I2CExtender i2c; @@ -381,48 +509,19 @@ namespace Configuration { i2c.validate(); i2c.init(); - // Expected register values (see datasheet): - // - // 4 invert - // 1 = invert, 0 = normal - // - // 6 config - // 1 = input, 0 = output - // - // 2 write - // 1 = high, 0 = low - // - // 0 read - // high = 1, low = 0 - { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x01); - i2c.claim(0); i2c.setupPin(0, Pin::Attr::Output); { Roundtrip rt; } - // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), - uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); } // Read will NOT trigger an update because we have an ISR to tell us when it changes: { bool readPin = i2c.readPin(0); - auto recv = Wire.Receive(); - - Assert(recv.size() == 0, "Expected single data request / response roundtrip, got %d", int(recv.size())); - Assert(readPin == true, "Expected 'true' on pin"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'false' on pin"); + Assert(pca.registersUsed() == 0, "Expected no-op for read"); } // Test write pin: @@ -431,24 +530,14 @@ namespace Configuration { i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04, "Expected no-op for read"); } { // Write to set it 'low'. i2c.writePin(0, false); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 0, "Expected write reg 0 = 0"); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04); } { // Write to set it 'low'. It's already low, no-op. @@ -456,141 +545,83 @@ namespace Configuration { i2c.flushWrites(); // no-op. - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0, "Expected no-op"); } { // Write to set it 'high'. i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04); } // NOTE: We ended with setting pin #0 to 'high' = 0x01 // Setup pin for reading: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - i2c.claim(1); i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), - uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x55); } // Setup another pin for reading with an invert mask and a PU: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x04); - i2c.claim(2); i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), - uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x55); } // Test read pin: { bool readPin = i2c.readPin(1); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + { Roundtrip rt; } + Assert(pca.registersUsed() == 0x0); Assert(readPin == false, "Expected 'true' on pin"); } // Test read pin: { bool readPin = i2c.readPin(2); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + { Roundtrip rt; } + Assert(pca.registersUsed() == 0x0); Assert(readPin == true, "Expected 'true' on pin"); } // Trigger an ISR, change both pins { - Wire.Send(0x02); - GPIONative::write(15, true); - GPIONative::write(15, false); + pca.setPadValue(1, true); + pca.setPadValue(2, true); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1); - Assert(recv[0] == 0); + Assert(pca.registersUsed() == 0x01); } // Test read pin: { bool readPin = i2c.readPin(1); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x0); Assert(readPin == true, "Expected 'true' on pin"); } // Test read pin: { bool readPin = i2c.readPin(2); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x0); Assert(readPin == false, "Expected 'true' on pin"); } } void HandleInterrupt(void* data) { ++(*reinterpret_cast(data)); } - volatile uint16_t currentInput = 0; - void WireResponseHandler(TwoWire* theWire, std::vector& data) { - if (data.size() == 1) { - if (data[0] == 0) { - Assert(theWire->SendSize() == 0); - theWire->Send(uint8_t(currentInput)); - data.clear(); - } else if (data[0] == 1) { - Assert(theWire->SendSize() == 0); - theWire->Send(uint8_t(currentInput >> 8)); - data.clear(); - } else if (data[0] >= 2 && data[0] <= 7) { - // ignore until next roundtrip - } else { - Assert(false, "Unknown register"); - } - } else if (data.size() == 2) { - if (data[0] >= 2 && data[0] <= 7) { - data.clear(); - } else { - Assert(false, "Unknown register"); - } - } else { - Assert(false, "Unknown size"); - } - } - Test(I2CExtender, ISRTriggerWithInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -606,6 +637,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -615,45 +647,24 @@ namespace Configuration { i2c.init(); { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - Wire.Send(0x00); - i2c.claim(9); i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } uint32_t isrCounter = 0; { - Wire.Send(0x00); - Wire.Send(0x00); - i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } { Roundtrip rt; } @@ -661,38 +672,49 @@ namespace Configuration { // Test read pin: { bool readPin = i2c.readPin(9); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); Assert(readPin == false, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x00); } // Change state, wait till roundtrip { - Wire.Send(0x00); - Wire.Send(0x02); - - // Trigger ISR pin 'falling' - GPIONative::write(15, true); - GPIONative::write(15, false); - { Roundtrip rt; } - - auto recv = Wire.Receive(); - Assert(recv.size() == 2); - Assert(recv[0] == 0); - Assert(recv[1] == 1); + pca.setPadValue(9, true); { Roundtrip rt; } // Test if ISR update went correctly: Assert(isrCounter == 1); + Assert(pca.registersUsed() == 0x03); // Test read pin: bool readPin = i2c.readPin(9); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); Assert(readPin == true, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x00); + } + + { + i2c.detachInterrupt(9); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0xFF); + } + + // Change state, wait till roundtrip + { + pca.setPadValue(9, false); + { Roundtrip rt; } + + // Test if ISR detach went correctly: + Assert(isrCounter == 1); + Assert(pca.registersUsed() == 0x03); } } Test(I2CExtender, ISRTriggerWithoutInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -708,6 +730,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -717,35 +740,19 @@ namespace Configuration { i2c.init(); { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - Wire.Send(0x00); - i2c.claim(9); i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } uint32_t isrCounter = 0; { // From this point on, we just need to respond to wire requests - currentInput = 0x0000; - Wire.Clear(); - Wire.SetResponseHandler(WireResponseHandler); - i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); } @@ -757,7 +764,8 @@ namespace Configuration { // Change state, wait till roundtrip { - currentInput = 0x0200; + pca.setPadValue(9, true); + { Roundtrip rt; } // Test if ISR update went correctly: @@ -766,17 +774,37 @@ namespace Configuration { // Test read pin: bool readPin = i2c.readPin(9); Assert(readPin == true, "Expected 'true' on pin"); + } + + { + pca.setPadValue(9, false); { Roundtrip rt; } // Test if ISR update went correctly: - Assert(isrCounter == 1); + Assert(isrCounter == 2); // Test read pin: bool readPin2 = i2c.readPin(9); - Assert(readPin2 == true, "Expected 'true' on pin"); + Assert(readPin2 == false, "Expected 'false' on pin"); } - Wire.Clear(); + { + i2c.detachInterrupt(9); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0xFF); + } + + // Change state, wait till roundtrip + { + pca.setPadValue(9, false); + { Roundtrip rt; } + + // Test if ISR detach went correctly: + Assert(isrCounter == 2); + } } } diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 2ada77bbe..35ff5cdad 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -28,7 +28,8 @@ void TwoWire::Clear() { std::lock_guard guard(mut); sentData.clear(); receivedData.clear(); - handler = nullptr; + handler = nullptr; + handlerUserData = nullptr; } // TwoWire interface: @@ -127,7 +128,7 @@ size_t TwoWire::write(uint8_t ch) { } if (handler) { - (*handler)(this, sentData); + (*handler)(this, sentData, handlerUserData); } return 0; } diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index b28f807ca..866597748 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -14,7 +14,8 @@ class TwoWire : public Stream { std::vector sentData; std::mutex mut; - using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data); + using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data, void* userData); + void* handlerUserData; ResponseHandler handler; public: @@ -28,7 +29,10 @@ class TwoWire : public Stream { std::vector Receive(); size_t ReceiveSize() { return sentData.size(); } void Clear(); - void SetResponseHandler(ResponseHandler handler) { this->handler = handler; } + void SetResponseHandler(ResponseHandler handler, void* userData) { + this->handlerUserData = userData; + this->handler = handler; + } // TwoWire interface: From cdb1f62827f1bc4eb0dcd808b7b6c7a72f1ba702 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Mar 2022 15:42:31 +0100 Subject: [PATCH 009/100] Fixed a few bugs in the pin extender --- FluidNC/src/Extenders/I2CExtender.cpp | 45 +++++++++----- FluidNC/src/Machine/I2CBus.cpp | 9 ++- FluidNC/src/Machine/LimitPin.cpp | 4 +- FluidNC/src/Machine/Motor.cpp | 11 +++- FluidNC/src/Machine/Motor.h | 2 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 69 ++++++++++++++++++++-- 6 files changed, 110 insertions(+), 30 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index fa7b2bbf8..d0bde9d96 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -20,9 +20,6 @@ namespace Extenders { uint8_t I2CExtender::I2CGetValue(uint8_t address, uint8_t reg) { auto bus = _i2cBus; - // First make sure the bus is empty, so we read that which we have written: - while (bus->read(address, ®, 1) != 0) {} - int err; if ((err = bus->write(address, ®, 1)) != 0) { log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); @@ -37,6 +34,8 @@ namespace Extenders { IOError(); } else { + // This log line will probably generate a stack overflow and way too much data. Use with care: + // log_info("Request address: " << int(address) << ", reg: " << int(reg) << " gives: " << int(result)); errorCount = 0; } return result; @@ -56,6 +55,8 @@ namespace Extenders { log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); IOError(); } else { + // This log line will probably generate a stack overflow and way too much data. Use with care: + // log_info("Set address: " << int(address) << ", reg: " << int(reg) << " to: " << int(value)); errorCount = 0; } } @@ -188,12 +189,27 @@ namespace Extenders { // If we don't have an ISR, we must update everything. Otherwise, we can cherry pick: bool handleInvertSoftware = (_invertReg == 0xFF); + uint8_t newBytes[8]; for (int i = 0; i < claimedValues; ++i) { - auto oldByte = _input.bytes[i]; auto newByte = I2CGetValue(address, currentRegister); if (handleInvertSoftware) { newByte ^= _invert.bytes[i]; } + newBytes[i] = newByte; + + currentRegister++; + if (currentRegister == registersPerDevice + _inputReg) { + ++address; + } + } + + // Remove the busy flag, keep the rest. If we don't do that here, we + // end up with a race condition if we use _status in the ISR. + _status &= ~0x10; + + for (int i = 0; i < claimedValues; ++i) { + auto oldByte = _input.bytes[i]; + auto newByte = newBytes[i]; if (oldByte != newByte) { // Handle ISR's: @@ -206,16 +222,11 @@ namespace Extenders { auto o = (oldByte & mask); auto n = (newByte & mask); if (o != n) { - isr.callback(isr.data); + isr.callback(isr.data); // bug; race condition } } } } - - currentRegister++; - if (currentRegister == registersPerDevice + _inputReg) { - ++address; - } } } } @@ -288,11 +299,11 @@ namespace Extenders { // Ensure data is available: std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); - xTaskCreatePinnedToCore(isrTaskLoop, // task - "i2cHandler", // name for task - configMINIMAL_STACK_SIZE + 512, // size of task stack - this, // parameters - 1, // priority + xTaskCreatePinnedToCore(isrTaskLoop, // task + "i2cHandler", // name for task + configMINIMAL_STACK_SIZE + 512 + 2048, // size of task stack + this, // parameters + 1, // priority &_isrHandler, SUPPORT_TASK_CORE // core ); @@ -391,6 +402,8 @@ namespace Extenders { Assert(mode == CHANGE, "Only mode CHANGE is allowed for pin extender ISR's."); Assert(index < 64 && index >= 0, "Pin index out of range"); + // log_debug("Attaching interrupt (I2C) on index " << int(index)); + ISRData& data = _isrData[index]; data.callback = callback; data.data = arg; @@ -409,6 +422,8 @@ namespace Extenders { void I2CExtender::detachInterrupt(pinnum_t index) { Assert(index < 64 && index >= 0, "Pin index out of range"); + // log_debug("Detaching interrupt (I2C) on index " << int(index)); + ISRData& data = _isrData[index]; data.callback = nullptr; data.data = nullptr; diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index daf5120a2..e62b9d1de 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -77,13 +77,12 @@ namespace Machine { // log_debug("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " // << (i2c ? "non null" : "null")); - for (size_t i = 0; i < count; ++i) { - if (i2c->requestFrom((int)address, 1) != 1) { - return i; - } + size_t c = i2c->requestFrom((int)address, count); + + for (size_t i = 0; i < c; ++i) { data[i] = i2c->read(); } - return count; + return c; } } diff --git a/FluidNC/src/Machine/LimitPin.cpp b/FluidNC/src/Machine/LimitPin.cpp index 804b65baa..3f4f0dfb8 100644 --- a/FluidNC/src/Machine/LimitPin.cpp +++ b/FluidNC/src/Machine/LimitPin.cpp @@ -46,6 +46,7 @@ namespace Machine { void IRAM_ATTR LimitPin::handleISR() { read(); + if (sys.state != State::Alarm && sys.state != State::ConfigAlarm && sys.state != State::Homing) { if (_pHardLimits && rtAlarm == ExecAlarm::None) { #if 0 @@ -58,7 +59,7 @@ namespace Machine { } #endif - // log_debug("Hard limits"); // This might not work from ISR context + // log_debug("Hard limits"); // This might not work from ISR context mc_reset(); // Initiate system kill. rtAlarm = ExecAlarm::HardLimit; // Indicate hard limit critical event } @@ -88,6 +89,7 @@ namespace Machine { if (_pin.undefined()) { return; } + set_bitnum(Axes::limitMask, _axis); _pin.report(_legend.c_str()); auto attr = Pin::Attr::Input | Pin::Attr::ISR; diff --git a/FluidNC/src/Machine/Motor.cpp b/FluidNC/src/Machine/Motor.cpp index 18f475925..63b48282e 100644 --- a/FluidNC/src/Machine/Motor.cpp +++ b/FluidNC/src/Machine/Motor.cpp @@ -10,10 +10,9 @@ #include "Axes.h" namespace Machine { + Motor::Motor(int axis, int motorNum) : _axis(axis), _motorNum(motorNum) {} + void Motor::group(Configuration::HandlerBase& handler) { - _negLimitPin = new LimitPin(_negPin, _axis, _motorNum, -1, _hardLimits); - _posLimitPin = new LimitPin(_posPin, _axis, _motorNum, 1, _hardLimits); - _allLimitPin = new LimitPin(_allPin, _axis, _motorNum, 0, _hardLimits); handler.item("limit_neg_pin", _negPin); handler.item("limit_pos_pin", _posPin); handler.item("limit_all_pin", _allPin); @@ -29,6 +28,12 @@ namespace Machine { } void Motor::init() { + log_debug("Initializing motor / limits..."); + + _negLimitPin = new LimitPin(_negPin, _axis, _motorNum, -1, _hardLimits); + _posLimitPin = new LimitPin(_posPin, _axis, _motorNum, 1, _hardLimits); + _allLimitPin = new LimitPin(_allPin, _axis, _motorNum, 0, _hardLimits); + if (strcmp(_driver->name(), "null_motor") != 0) { set_bitnum(Machine::Axes::motorMask, _axis + 16 * _motorNum); } diff --git a/FluidNC/src/Machine/Motor.h b/FluidNC/src/Machine/Motor.h index 7fc1fed69..2a751d9d2 100644 --- a/FluidNC/src/Machine/Motor.h +++ b/FluidNC/src/Machine/Motor.h @@ -25,7 +25,7 @@ namespace Machine { int _motorNum; public: - Motor(int axis, int motorNum) : _axis(axis), _motorNum(motorNum) {} + Motor(int axis, int motorNum); MotorDrivers::MotorDriver* _driver = nullptr; float _pulloff = 1.0f; // mm diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp index 9212e84de..95e6f8dbb 100644 --- a/FluidNC/test/Extender/I2CExtenderTests.cpp +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -653,7 +653,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } uint32_t isrCounter = 0; @@ -664,7 +665,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } { Roundtrip rt; } @@ -697,7 +699,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } // Change state, wait till roundtrip @@ -746,7 +749,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } uint32_t isrCounter = 0; @@ -795,7 +799,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } // Change state, wait till roundtrip @@ -807,4 +812,58 @@ namespace Configuration { Assert(isrCounter == 2); } } + + void ReadInISRHandler(void* data) { + auto i2c = static_cast(data); + auto value = i2c->readPin(9); + Assert(value == true); + } + + Test(I2CExtender, ReadInISR) { + std::lock_guard guard(single_thread); + GPIONative::initialize(); + PCA9539Emulator pca(15); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); + } + + { + pca.setPadValue(9, false); + i2c.attachInterrupt(9, ReadInISRHandler, &i2c, CHANGE); + pca.setPadValue(9, true); + i2c.detachInterrupt(9); + pca.setPadValue(9, false); + } + } } From 581503779544283e385ae352c1819f548faad2d2 Mon Sep 17 00:00:00 2001 From: bdring Date: Sat, 16 Apr 2022 14:26:59 -0500 Subject: [PATCH 010/100] WIP --- FluidNC/src/Extenders/I2CExtender.cpp | 14 +++++++++++++- FluidNC/src/Extenders/I2CExtender.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index d0bde9d96..cc136c0e0 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -13,7 +13,9 @@ #include namespace Extenders { - EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, + { int(I2CExtenderDevice::PCA9555), "pca9555" }, + EnumItem(int(I2CExtenderDevice::Unknown)) }; I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} @@ -291,6 +293,16 @@ namespace Extenders { _operationReg = 6; break; + case I2CExtenderDevice::PCA9555: + // See data sheet page 7+: + _address = 0x20 + _deviceId; + _ports = 16; + _inputReg = 0; + _outputReg = 2; + _invertReg = 4; + _operationReg = 6; + break; + default: Assert(false, "Pin extender device is not supported!"); break; diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index d5bacc1b9..74ebb2f75 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -13,12 +13,14 @@ namespace Pins { class PCA9539PinDetail; + class PCA9555PinDetail; } namespace Extenders { enum class I2CExtenderDevice { Unknown, PCA9539, + PCA9555, }; // Pin extenders... From 476587bd324251bb67ed91c8a3130556f8e44c3b Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Sat, 5 Feb 2022 22:41:11 +0100 Subject: [PATCH 011/100] Fixed ISR mess the nasty way. (#273) Co-authored-by: Stefan de Bruijn --- FluidNC/src/ControlPin.cpp | 4 +-- FluidNC/src/ControlPin.h | 4 ++- FluidNC/src/Machine/LimitPin.cpp | 2 +- FluidNC/src/Machine/LimitPin.h | 4 ++- FluidNC/src/Pin.h | 42 +++++++++++++++++++++----------- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/FluidNC/src/ControlPin.cpp b/FluidNC/src/ControlPin.cpp index 42bee654c..adbc60203 100644 --- a/FluidNC/src/ControlPin.cpp +++ b/FluidNC/src/ControlPin.cpp @@ -23,9 +23,9 @@ void ControlPin::init() { attr = attr | Pin::Attr::PullUp; } _pin.setAttr(attr); - _pin.attachInterrupt(this, CHANGE); + _pin.attachInterrupt(ISRHandler, CHANGE, this); _rtVariable = false; - _value = _pin.read(); + _value = _pin.read(); // Control pins must start in inactive state if (_value) { log_error(_legend << " pin is active at startup"); diff --git a/FluidNC/src/ControlPin.h b/FluidNC/src/ControlPin.h index 360047a08..5d3cf864e 100644 --- a/FluidNC/src/ControlPin.h +++ b/FluidNC/src/ControlPin.h @@ -1,6 +1,7 @@ #pragma once #include "Pin.h" +#include // IRAM_ATTR class ControlPin { private: @@ -9,7 +10,8 @@ class ControlPin { volatile bool& _rtVariable; const char* _legend; - void handleISR(); + void IRAM_ATTR handleISR(); + CreateISRHandlerFor(ControlPin, handleISR); public: ControlPin(volatile bool& rtVariable, const char* legend, char letter) : diff --git a/FluidNC/src/Machine/LimitPin.cpp b/FluidNC/src/Machine/LimitPin.cpp index 88237cb75..a06e2d739 100644 --- a/FluidNC/src/Machine/LimitPin.cpp +++ b/FluidNC/src/Machine/LimitPin.cpp @@ -95,7 +95,7 @@ namespace Machine { attr = attr | Pin::Attr::PullUp; } _pin.setAttr(attr); - _pin.attachInterrupt(this, CHANGE); + _pin.attachInterrupt(ISRHandler, CHANGE, this); read(); } diff --git a/FluidNC/src/Machine/LimitPin.h b/FluidNC/src/Machine/LimitPin.h index a3bbeabab..d0a6631df 100644 --- a/FluidNC/src/Machine/LimitPin.h +++ b/FluidNC/src/Machine/LimitPin.h @@ -22,6 +22,8 @@ namespace Machine { void IRAM_ATTR handleISR(); + CreateISRHandlerFor(LimitPin, handleISR); + void read(); public: @@ -33,7 +35,7 @@ namespace Machine { void init(); bool get() { return _value; } - void makeDualMask(); // makes this a mask for motor0 and motor1 + void makeDualMask(); // makes this a mask for motor0 and motor1 ~LimitPin(); }; diff --git a/FluidNC/src/Pin.h b/FluidNC/src/Pin.h index e2e2111ef..c20b01a4e 100644 --- a/FluidNC/src/Pin.h +++ b/FluidNC/src/Pin.h @@ -19,6 +19,21 @@ // Forward declarations: class String; +// Yuck, yuck, yuck... apparently we can't create a template with an IRAM_ATTR, because GCC refuses to obide +// by the attributes. In other words, _all_ templates are out when using an ISR! This define makes an anonymous +// method in the class, which can be used to wrap a single function. It can then be used by running the attachInterrupt +// with ISRHandler. +// +// Usage: +// - In header file (private / protected members) or in cpp file in anonymous namespace (public members) +// CreateISRHandlerFor(LimitPin, handleISR); +// - When attaching an ISR: _pin.attachInterrupt(ISRHandler, CHANGE, this); +// +// I'd rather not use any defines, but templates... but apparently there's no choice here. Let's just make it as safe +// as possible... +#define CreateISRHandlerFor(className, methodName) \ + static void IRAM_ATTR ISRHandler(void* data) { static_cast(data)->methodName(); } + // Pin class. A pin is basically a thing that can 'output', 'input' or do both. GPIO on an ESP32 comes to mind, // but there are way more possible pins. Think about I2S/I2C/SPI extenders, RS485 driven pin devices and even // WiFi wall sockets. @@ -38,17 +53,21 @@ class String; // one-stop-go-to-shop for an pin. class Pin { // Helper for handling callbacks and mapping them to the proper class: - template - struct InterruptCallbackHelper { - static void IRAM_ATTR callback(void* ptr) { (static_cast(ptr)->*Callback)(); } - }; + // + // Take note: this is placing the code in FLASH instead of IRAM, like it should. Use the #define's above + // until this is fixed! + + // template + // struct InterruptCallbackHelper { + // static void IRAM_ATTR callback(void* ptr) { (static_cast(ptr)->*Callback)(); } + // }; // Helper for handling callbacks and mapping them to the proper class. This one is just meant // for backward compatibility: - template - struct InterruptCallbackHelper2 { - static void IRAM_ATTR callback(void* /*ptr*/) { Callback(); } - }; + // template + // struct InterruptCallbackHelper2 { + // static void IRAM_ATTR callback(void* /*ptr*/) { Callback(); } + // }; // The undefined pin and error pin are two special pins. Error pins always throw an error when they are used. // These are useful for unit testing, and for initializing pins that _always_ have to be defined by a user @@ -103,7 +122,7 @@ class Pin { // External libraries normally use digitalWrite, digitalRead and setMode. Since we cannot handle that behavior, we // just give back the pinnum_t for getNative. inline pinnum_t getNative(Capabilities expectedBehavior) const { - Assert(_detail->capabilities().has(expectedBehavior), "Requested pin %s does not have the expected behavior.",name().c_str()); + Assert(_detail->capabilities().has(expectedBehavior), "Requested pin %s does not have the expected behavior.", name().c_str()); return _detail->_index; } @@ -123,11 +142,6 @@ class Pin { // ISR handlers. Map methods on 'this' types. - template - void attachInterrupt(ThisType* arg, int mode) { - _detail->attachInterrupt(InterruptCallbackHelper::callback, arg, mode); - } - // Backward compatibility ISR handler: void attachInterrupt(void (*callback)(void*), int mode, void* arg = nullptr) const { _detail->attachInterrupt(callback, arg, mode); } From ab17cdddd0faf88966b063066521a7d48bb71116 Mon Sep 17 00:00:00 2001 From: bdring Date: Sat, 5 Feb 2022 15:43:47 -0600 Subject: [PATCH 012/100] Fix VFD Delays (#270) - A reset during a spin up/down delay was causing a crash. - Also added delay register reporting to Huanyang - Note realtime commands are not handled during delays now. --- FluidNC/src/Spindles/HuanyangSpindle.cpp | 143 +++++++++++++---------- FluidNC/src/Spindles/VFDSpindle.cpp | 14 ++- 2 files changed, 88 insertions(+), 69 deletions(-) diff --git a/FluidNC/src/Spindles/HuanyangSpindle.cpp b/FluidNC/src/Spindles/HuanyangSpindle.cpp index dd647143b..7664ca33a 100644 --- a/FluidNC/src/Spindles/HuanyangSpindle.cpp +++ b/FluidNC/src/Spindles/HuanyangSpindle.cpp @@ -220,91 +220,108 @@ namespace Spindles { data.msg[4] = 0x00; data.msg[5] = 0x00; - if (index == -1) { - // Max frequency - data.msg[3] = 5; // PD005: max frequency the VFD will allow. Normally 400. + switch (index) { + case -1: + data.msg[3] = 5; // PD005: max frequency the VFD will allow. Normally 400. - return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { - uint16_t value = (response[4] << 8) | response[5]; + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint16_t value = (response[4] << 8) | response[5]; - // Set current RPM value? Somewhere? - auto huanyang = static_cast(vfd); - huanyang->_maxFrequency = value; - return true; - }; + // Set current RPM value? Somewhere? + auto huanyang = static_cast(vfd); + huanyang->_maxFrequency = value; + return true; + }; + break; + case -2: + data.msg[3] = 11; // PD011: frequency lower limit. Normally 0. - } else if (index == -2) { - // Min Frequency - data.msg[3] = 11; // PD011: frequency lower limit. Normally 0. + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint16_t value = (response[4] << 8) | response[5]; - return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { - uint16_t value = (response[4] << 8) | response[5]; + // Set current RPM value? Somewhere? + auto huanyang = static_cast(vfd); + huanyang->_minFrequency = value; - // Set current RPM value? Somewhere? - auto huanyang = static_cast(vfd); - huanyang->_minFrequency = value; + log_info(huanyang->name() << " PD005,PD011 Freq range (" << (huanyang->_minFrequency / 100) << "," + << (huanyang->_maxFrequency / 100) << ") Hz" + << " (" << (huanyang->_minFrequency / 100 * 60) << "," << (huanyang->_maxFrequency / 100 * 60) + << ") RPM"); - log_info(huanyang->name() << " PD005,PD011 Freq range (" << (huanyang->_minFrequency / 100) << "," - << (huanyang->_maxFrequency / 100) << ") Hz"); - log_info(huanyang->name() << " RPM range (" << (huanyang->_minFrequency / 100 * 60) << "," - << (huanyang->_maxFrequency / 100 * 60) << ") RPM"); + return true; + }; + break; + case -3: + data.msg[3] = 144; // PD144: max rated motor revolution at 50Hz => 24000@400Hz = 3000@50HZ - return true; - }; - } else if (index == -3) { - // Max rated revolutions @ 50Hz + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint16_t value = (response[4] << 8) | response[5]; - data.msg[3] = 144; // PD144: max rated motor revolution at 50Hz => 24000@400Hz = 3000@50HZ + // Set current RPM value? Somewhere? + auto huanyang = static_cast(vfd); + huanyang->_maxRpmAt50Hz = value; - return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { - uint16_t value = (response[4] << 8) | response[5]; + log_info(huanyang->name() << " PD144 Rated RPM @ 50Hz:" << huanyang->_maxRpmAt50Hz); - // Set current RPM value? Somewhere? - auto huanyang = static_cast(vfd); - huanyang->_maxRpmAt50Hz = value; + // Regarding PD144, the 2 versions of the manuals both say "This is set according to the + // actual revolution of the motor. The displayed value is the same as this set value. It + // can be used as a monitoring parameter, which is convenient to the user. This set value + // corresponds to the revolution at 50Hz". - log_info(huanyang->name() << " PD144 Rated RPM @ 50Hz:" << huanyang->_maxRpmAt50Hz); + // Calculate the VFD settings: + huanyang->updateRPM(); - // Regarding PD144, the 2 versions of the manuals both say "This is set according to the - // actual revolution of the motor. The displayed value is the same as this set value. It - // can be used as a monitoring parameter, which is convenient to the user. This set value - // corresponds to the revolution at 50Hz". + return true; + }; + break; + case -4: + data.rx_length = 5; + data.msg[3] = 143; // PD143: 4 or 2 poles in motor. Default is 4. A spindle being 24000RPM@400Hz implies 2 poles - // Calculate the VFD settings: - huanyang->updateRPM(); + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint8_t value = response[4]; // Single byte response. + auto huanyang = static_cast(vfd); + // Sanity check. We expect something like 2 or 4 poles. + if (value <= 4 && value >= 2) { + // Set current RPM value? Somewhere? - return true; - }; - } + huanyang->_numberPoles = value; - // The number of poles seems to be over constrained information with PD144. If we're wrong, here's how - // to get this information. Note that you probably have to call 'updateRPM' here then: - // -- + log_info(huanyang->name() << " PD143 Poles:" << huanyang->_numberPoles); - else if (index == -4) { - // Number Poles - data.rx_length = 5; - data.msg[3] = 143; // PD143: 4 or 2 poles in motor. Default is 4. A spindle being 24000RPM@400Hz implies 2 poles + huanyang->updateRPM(); - return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { - uint8_t value = response[4]; // Single byte response. - auto huanyang = static_cast(vfd); - // Sanity check. We expect something like 2 or 4 poles. - if (value <= 4 && value >= 2) { - // Set current RPM value? Somewhere? + return true; + } else { + log_error(huanyang->name() << " PD143 Poles: expected 2-4, got:" << value); + return false; + } + }; + break; + case -5: + data.msg[3] = 14; // Accel value displayed is X.X - huanyang->_numberPoles = value; + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint16_t value = (response[4] << 8) | response[5]; - log_info(huanyang->name() << " PD143 Poles:" << huanyang->_numberPoles); + auto huanyang = static_cast(vfd); + log_info(huanyang->name() << " PD014 Accel:" << float(value) / 10.0); + return true; + }; + break; + case -6: + data.msg[3] = 15; // Decel alue displayed is X.X - huanyang->updateRPM(); + return [](const uint8_t* response, Spindles::VFD* vfd) -> bool { + uint16_t value = (response[4] << 8) | response[5]; + auto huanyang = static_cast(vfd); + log_info(huanyang->name() << " PD015 Decel:" << float(value) / 10.0); return true; - } else { - log_error(huanyang->name() << " PD143 Poles: expected 2-4, got:" << value); - return false; - } - }; + }; + break; + default: + break; } // Done. diff --git a/FluidNC/src/Spindles/VFDSpindle.cpp b/FluidNC/src/Spindles/VFDSpindle.cpp index 04687b4aa..d28149fad 100644 --- a/FluidNC/src/Spindles/VFDSpindle.cpp +++ b/FluidNC/src/Spindles/VFDSpindle.cpp @@ -304,6 +304,7 @@ namespace Spindles { void VFD::config_message() { _uart->config_message(name(), " Spindle "); } void VFD::setState(SpindleState state, SpindleSpeed speed) { + log_info("VFD setState:" << uint8_t(state) << " SpindleSpeed:" << speed); if (sys.abort) { return; // Block during abort. } @@ -346,12 +347,13 @@ namespace Spindles { #ifdef DEBUG_VFD log_debug("Syncing speed. Requested: " << int(dev_speed) << " current:" << int(_sync_dev_speed)); #endif - if (!mc_dwell(500)) { - // Something happened while we were dwelling, like a safety door. - unchanged = limit; - last = _sync_dev_speed; - break; - } + // if (!mc_dwell(500)) { + // // Something happened while we were dwelling, like a safety door. + // unchanged = limit; + // last = _sync_dev_speed; + // break; + // } + delay(500); // unchanged counts the number of consecutive times that we see the same speed unchanged = (_sync_dev_speed == last) ? unchanged + 1 : 0; From 4b3a175296de4c09c4e219bcbbf50c0f23c422bb Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sat, 5 Feb 2022 11:44:31 -1000 Subject: [PATCH 013/100] Fixed URLs in embedded page, added python build script (#269) --- FluidNC/src/WebUI/NoFile.h | 858 ++++++++++++++++++------------------- build-release.py | 8 + embedded/build.py | 42 ++ embedded/package-lock.json | 765 +++++++++++++++++++++------------ embedded/tool.html.gz | Bin 6728 -> 6737 bytes embedded/www/js/script.js | 6 +- embedded/www/tool.html | 2 +- 7 files changed, 980 insertions(+), 701 deletions(-) create mode 100644 embedded/build.py diff --git a/FluidNC/src/WebUI/NoFile.h b/FluidNC/src/WebUI/NoFile.h index 7bbee2a21..d03b2cf36 100644 --- a/FluidNC/src/WebUI/NoFile.h +++ b/FluidNC/src/WebUI/NoFile.h @@ -1,436 +1,434 @@ +// Embedded web page to load the index,html.gz for ESP3D-WEBUI. +// Generated from the code in /embedded/ . Do not edit manually. + // Copyright (c) 2014 Luc Lebosse. All rights reserved. -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. +// License: GPL version 2.1 or (at your option) any later version. #pragma once // clang-format off -//data generated by https://github.com/AraHaan/bin2c -//bin2c Conversion Tool v0.14.0 - Windows - [FINAL]. - -/* Generated by bin2c, do not edit manually */ - -/* Contents of file tool.html.gz */ -const int PAGE_NOFILES_SIZE = 6728; -const char PAGE_NOFILES[6728] PROGMEM = { - 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xED, 0x3C, 0x89, 0x72, 0xDB, 0xC6, - 0x92, 0xBF, 0x82, 0x20, 0x15, 0x93, 0x58, 0x02, 0x24, 0x2E, 0xDE, 0xA2, 0xBC, 0x49, 0x2C, 0x27, - 0xDA, 0xB2, 0x63, 0x97, 0x24, 0xAF, 0xF7, 0x95, 0xE3, 0x52, 0x81, 0xC4, 0x50, 0xC4, 0x1A, 0x04, - 0x28, 0x60, 0x28, 0x4A, 0x96, 0xB9, 0xDF, 0xBE, 0xDD, 0x3D, 0x83, 0x8B, 0x97, 0x8E, 0xE7, 0xB7, - 0x2F, 0x5B, 0xF5, 0xA2, 0x90, 0x00, 0xE6, 0xE8, 0xE9, 0xE9, 0xBB, 0x1B, 0x43, 0x1F, 0xCD, 0xF8, - 0x3C, 0x3C, 0x3E, 0x9A, 0x31, 0xCF, 0x3F, 0x3E, 0x4A, 0xF9, 0x5D, 0xC8, 0x8E, 0xB1, 0xE5, 0x7E, - 0x1A, 0x47, 0xDC, 0x98, 0x7A, 0xF3, 0x20, 0xBC, 0x1B, 0xA4, 0x5E, 0x94, 0x1A, 0x29, 0x4B, 0x82, - 0xE9, 0xD0, 0x98, 0xA7, 0x06, 0x67, 0xB7, 0xDC, 0x48, 0x83, 0xAF, 0xCC, 0xF0, 0xFC, 0xFF, 0x5E, - 0xA6, 0x7C, 0x60, 0x99, 0xE6, 0x4F, 0x43, 0x63, 0xC5, 0xC6, 0x5F, 0x02, 0xBE, 0xA7, 0x97, 0xC0, - 0x61, 0x2B, 0x3C, 0x2E, 0x6E, 0xD7, 0xE3, 0xD8, 0xBF, 0xAB, 0x2C, 0xA1, 0xFE, 0xCE, 0xC2, 0x1B, - 0xC6, 0x83, 0x89, 0xA7, 0xFC, 0xC1, 0x96, 0x4C, 0xD5, 0xF3, 0x67, 0xFD, 0xE7, 0x24, 0xF0, 0x42, - 0xBD, 0x84, 0x43, 0x09, 0x96, 0xBB, 0xB8, 0x1D, 0x86, 0x41, 0xC4, 0x8C, 0x19, 0x0B, 0xAE, 0x66, - 0xB0, 0x56, 0xD3, 0xB5, 0x7B, 0xED, 0xAE, 0xE5, 0x3A, 0xC3, 0x49, 0x1C, 0xC6, 0xC9, 0xE0, 0x47, - 0xC7, 0x71, 0x86, 0x63, 0x6F, 0xF2, 0xE5, 0x2A, 0x89, 0x97, 0x91, 0x6F, 0xC8, 0xD6, 0xE9, 0x74, - 0xBA, 0xE6, 0xDE, 0x38, 0x64, 0xF7, 0xE3, 0x38, 0xF1, 0x59, 0x32, 0x30, 0x87, 0xE2, 0xC6, 0x48, - 0x17, 0xDE, 0x24, 0x88, 0xAE, 0xA0, 0x61, 0xEE, 0xDD, 0x1A, 0xAB, 0xC0, 0xE7, 0x33, 0xDA, 0xC1, - 0x9A, 0xFB, 0xF7, 0xAB, 0x59, 0xC0, 0x19, 0x8D, 0x60, 0x83, 0x28, 0x5E, 0x25, 0xDE, 0x62, 0xB8, - 0xF0, 0x7C, 0x1F, 0x87, 0xDB, 0xF3, 0xF9, 0x9A, 0xCF, 0xEE, 0x69, 0xF3, 0x5E, 0x18, 0x5C, 0x45, - 0x83, 0x90, 0x4D, 0xF9, 0xBA, 0x49, 0x8B, 0x1C, 0x73, 0xDC, 0xEF, 0x31, 0x4F, 0x8E, 0xB9, 0xAF, - 0x6F, 0x35, 0xCD, 0xF2, 0x26, 0x62, 0x42, 0x75, 0x54, 0xDE, 0x34, 0xBB, 0xCF, 0x96, 0xEA, 0xED, - 0xDF, 0xF3, 0x0D, 0x4B, 0x90, 0x64, 0xA1, 0x44, 0x81, 0xC7, 0x8B, 0x6C, 0x5B, 0x70, 0x3B, 0xB0, - 0x16, 0xB7, 0x4A, 0x1A, 0x87, 0x81, 0xAF, 0xFC, 0xE8, 0xFB, 0xBE, 0xC4, 0xCD, 0x48, 0x79, 0x12, - 0x2C, 0x98, 0x9F, 0x23, 0x34, 0x88, 0xF8, 0xCC, 0x88, 0xA7, 0x06, 0xBF, 0x5B, 0xB0, 0x7A, 0xEC, - 0xFB, 0xDA, 0xFD, 0x0E, 0xF2, 0xF5, 0xF1, 0x6F, 0xED, 0xDD, 0x2F, 0xE2, 0x34, 0xE0, 0x41, 0x1C, - 0x0D, 0x12, 0x16, 0x7A, 0x3C, 0xB8, 0x61, 0x43, 0x3F, 0x48, 0x17, 0xA1, 0x77, 0x37, 0x18, 0x87, - 0xF1, 0xE4, 0x4B, 0x4E, 0x1E, 0x64, 0xBA, 0x62, 0xB5, 0x01, 0x73, 0xA2, 0x90, 0xCF, 0x26, 0x71, - 0xE2, 0xD1, 0xC4, 0x28, 0x8E, 0x58, 0xC6, 0xAB, 0xC9, 0x64, 0xB2, 0x6E, 0x7A, 0x13, 0x84, 0x73, - 0x5F, 0x30, 0x6A, 0x07, 0xFB, 0x4C, 0xD3, 0xCC, 0x06, 0x2A, 0x9E, 0xEE, 0x0D, 0xA6, 0xF1, 0x64, - 0x99, 0xC2, 0x75, 0x16, 0x03, 0x05, 0x4A, 0x53, 0xD7, 0xCD, 0x85, 0x17, 0xB1, 0xF0, 0x7E, 0xEE, - 0x25, 0x57, 0x41, 0x64, 0x8C, 0x63, 0xCE, 0xE3, 0xF9, 0xC0, 0x06, 0x64, 0x76, 0xCB, 0x84, 0xA4, - 0xD6, 0x06, 0xA5, 0x32, 0x1A, 0x26, 0x9E, 0x1F, 0x2C, 0xD3, 0x01, 0xCA, 0x5C, 0x26, 0xEC, 0xE3, - 0xF8, 0xD6, 0x48, 0x67, 0x9E, 0x1F, 0xAF, 0x06, 0xA6, 0x82, 0xB3, 0xF0, 0x93, 0x5C, 0x8D, 0xBD, - 0xBA, 0xA9, 0xE3, 0x5F, 0xD3, 0x6C, 0x6B, 0xC3, 0xC7, 0x0C, 0x92, 0x98, 0x1A, 0xA4, 0x18, 0x39, - 0xD5, 0x80, 0x60, 0x59, 0x07, 0x0A, 0x02, 0xB4, 0xDD, 0x6F, 0x53, 0xF4, 0xB0, 0xA0, 0xB7, 0xF1, - 0x2F, 0xDB, 0x81, 0x6C, 0x2C, 0xED, 0x09, 0xE4, 0xC2, 0x48, 0x50, 0x8C, 0xB2, 0xDD, 0x39, 0x48, - 0x9B, 0xA2, 0x0F, 0xA5, 0x78, 0x47, 0x97, 0xA4, 0xE4, 0xA6, 0x44, 0x4D, 0xE3, 0x64, 0x0E, 0x8B, - 0x44, 0x3C, 0x89, 0xC3, 0xFB, 0xAA, 0x24, 0x08, 0x4D, 0xF2, 0x96, 0x3C, 0x1E, 0x4A, 0xB9, 0x75, - 0x90, 0x90, 0xD9, 0x76, 0x3A, 0xB8, 0x1B, 0x1B, 0x1A, 0x9E, 0xA4, 0xDC, 0xED, 0x76, 0x7B, 0x1F, - 0x23, 0x8B, 0xD6, 0x60, 0xEE, 0x5D, 0x31, 0x21, 0x67, 0xDB, 0xEC, 0x05, 0x91, 0x7B, 0x1C, 0x7B, - 0x83, 0x28, 0x65, 0x5C, 0xD9, 0xC3, 0xBF, 0x6E, 0x95, 0xCB, 0x0F, 0x8E, 0x35, 0x62, 0x83, 0x27, - 0x60, 0xD0, 0x84, 0xEE, 0x94, 0x99, 0xA3, 0x30, 0x2F, 0x65, 0x06, 0xC8, 0x6A, 0xBC, 0xE4, 0x4A, - 0xD3, 0x6A, 0xA7, 0x7A, 0x01, 0x77, 0xAB, 0xAF, 0x4A, 0x70, 0xA1, 0x05, 0xF7, 0x55, 0x56, 0x77, - 0x3A, 0xDE, 0x94, 0xF5, 0x87, 0x30, 0x03, 0x29, 0x09, 0x56, 0xED, 0x19, 0x5B, 0xD3, 0x4D, 0xE8, - 0xEC, 0x65, 0x1D, 0x96, 0x69, 0xEB, 0x56, 0xB7, 0xAD, 0xDB, 0x8E, 0xA3, 0x37, 0x3B, 0x9A, 0xC4, - 0x01, 0x69, 0xBD, 0xD8, 0xD0, 0x33, 0x21, 0xBE, 0x63, 0x1E, 0xE5, 0xA2, 0x10, 0x44, 0xC4, 0x4F, - 0x21, 0x11, 0xD5, 0xC1, 0xA6, 0xE0, 0xFC, 0x4A, 0xB0, 0xDA, 0x35, 0xCD, 0x61, 0xC9, 0x96, 0x4E, - 0x58, 0xC4, 0x59, 0xB2, 0x69, 0xDE, 0xE6, 0x81, 0xEF, 0x87, 0x4C, 0xB8, 0xA4, 0x78, 0x39, 0x99, - 0x19, 0x68, 0x11, 0x80, 0x9E, 0x73, 0x2F, 0x0A, 0x16, 0xCB, 0x90, 0xEC, 0xCB, 0x70, 0x7F, 0xCF, - 0x64, 0x99, 0xA4, 0x40, 0xA2, 0x45, 0x1C, 0x10, 0xF0, 0x47, 0x4A, 0x0C, 0xF1, 0x6D, 0xE1, 0x25, - 0x80, 0xD1, 0xF0, 0x80, 0x3F, 0x78, 0xA2, 0x3C, 0xEF, 0x10, 0xC1, 0x79, 0xFC, 0xD5, 0x58, 0xA6, - 0xE8, 0x91, 0x58, 0xC8, 0x26, 0x5C, 0xA0, 0x83, 0x7B, 0xDD, 0x6A, 0xDC, 0x6C, 0x20, 0x9A, 0x1B, - 0x8B, 0x04, 0xB6, 0x91, 0xDC, 0x1D, 0x36, 0xA4, 0x8E, 0xD3, 0xF5, 0xC6, 0xDD, 0x0D, 0xF3, 0x60, - 0xB3, 0x8E, 0xEF, 0xB9, 0x15, 0x28, 0xD2, 0xD8, 0xEA, 0x95, 0x36, 0x61, 0x75, 0x2B, 0x4D, 0x64, - 0x80, 0x2B, 0x4D, 0x83, 0x1D, 0x33, 0x07, 0xDB, 0x33, 0xB7, 0x4C, 0xF7, 0x0E, 0x64, 0xED, 0x5E, - 0xC7, 0xEC, 0x9B, 0x1B, 0xC8, 0x5A, 0xB6, 0x3D, 0x76, 0x4D, 0x42, 0x36, 0x98, 0x5F, 0xDD, 0x4B, - 0xA6, 0xCE, 0xBC, 0x68, 0xD3, 0x6C, 0x77, 0x72, 0xEB, 0x55, 0xD6, 0x7F, 0x72, 0x12, 0x62, 0xAE, - 0x44, 0x61, 0x87, 0x3D, 0x31, 0xF1, 0x6F, 0x63, 0xDD, 0xCE, 0x04, 0xFF, 0x9E, 0xAD, 0x4E, 0x28, - 0x1F, 0x57, 0x09, 0xBB, 0x7B, 0x8A, 0xD9, 0xA8, 0x4C, 0x24, 0xAC, 0x09, 0xCD, 0xC3, 0xDB, 0x76, - 0x4C, 0xA9, 0x84, 0xD9, 0xD8, 0x87, 0xB6, 0xF9, 0xCF, 0xDC, 0x51, 0x08, 0x48, 0x81, 0x86, 0x7C, - 0xD1, 0x8B, 0xDB, 0x41, 0x35, 0x1E, 0x20, 0xCF, 0x5F, 0x74, 0x56, 0xA4, 0x06, 0xFB, 0x82, 0x68, - 0xB1, 0xE4, 0x9F, 0x30, 0x76, 0x19, 0x4D, 0x83, 0x90, 0x7D, 0x1E, 0x0C, 0xB2, 0xFD, 0xE0, 0xA3, - 0xB1, 0x5C, 0x84, 0xB1, 0xE7, 0x1B, 0xE3, 0x25, 0xD8, 0x9C, 0x7F, 0x99, 0xA5, 0xFF, 0x5B, 0xB3, - 0x34, 0x3C, 0xA8, 0xDC, 0xED, 0xF1, 0xC4, 0xF4, 0xD9, 0x86, 0x92, 0xB9, 0x9D, 0x71, 0xCF, 0xF7, - 0x9E, 0xC4, 0x54, 0xE9, 0x05, 0xFF, 0xC5, 0xDA, 0xBF, 0x0E, 0x6B, 0x1D, 0x6B, 0x6C, 0xFA, 0x9B, - 0x31, 0xA8, 0x35, 0xEE, 0xF8, 0xBD, 0xF6, 0xD3, 0x58, 0x2B, 0xB4, 0xFD, 0x5F, 0xAC, 0xFD, 0x8B, - 0xB3, 0xD6, 0xEE, 0xF4, 0xBD, 0xF1, 0x24, 0x4B, 0x5C, 0xA6, 0x71, 0x0C, 0x14, 0x39, 0x90, 0xB7, - 0x58, 0x5D, 0xB3, 0xB7, 0x0B, 0xF6, 0x23, 0x52, 0x97, 0xAD, 0x04, 0xE4, 0x9F, 0xB0, 0xE4, 0x3C, - 0xF6, 0xBD, 0x22, 0xD9, 0x21, 0x92, 0xE5, 0x59, 0xF1, 0x34, 0xB8, 0x65, 0xFE, 0xF0, 0x2B, 0xC4, - 0xEC, 0x3E, 0xBB, 0xC5, 0x32, 0x02, 0x48, 0xA2, 0xC4, 0x4A, 0xC0, 0x32, 0x31, 0x15, 0xC5, 0x1C, - 0x0B, 0x44, 0x16, 0x1B, 0xCC, 0x61, 0x51, 0x71, 0xC8, 0xF2, 0x24, 0xBA, 0x47, 0xC9, 0x9F, 0x86, - 0xE0, 0x52, 0x29, 0x83, 0xDA, 0x99, 0x11, 0x6F, 0xB7, 0x96, 0xDD, 0xAD, 0xAB, 0x49, 0x54, 0x29, - 0x5D, 0x00, 0x81, 0xBB, 0xDF, 0x93, 0xE5, 0x59, 0x66, 0x35, 0x03, 0xAC, 0x64, 0x87, 0xE5, 0x4E, - 0xA1, 0x6B, 0x7B, 0xE7, 0xCA, 0xEE, 0x7D, 0xD3, 0x07, 0x76, 0x41, 0xC7, 0x3C, 0x0A, 0x2D, 0xE5, - 0xC9, 0x98, 0x6F, 0x58, 0x28, 0xF8, 0x66, 0x25, 0x6A, 0xB0, 0xB5, 0xE1, 0x76, 0xCD, 0x41, 0x28, - 0xBF, 0x20, 0x4D, 0xC6, 0xF4, 0x1D, 0xE4, 0xF8, 0x71, 0xCA, 0xF0, 0x2F, 0xA3, 0x03, 0x66, 0xD4, - 0x25, 0x29, 0xB1, 0xE5, 0x82, 0x99, 0x90, 0x50, 0x34, 0xB4, 0x53, 0x48, 0x6C, 0xFC, 0xDB, 0x97, - 0x24, 0x3F, 0x91, 0x7C, 0x95, 0x5C, 0x74, 0x8A, 0x7F, 0x19, 0x7A, 0xD5, 0x4A, 0x80, 0x29, 0xB1, - 0xCB, 0x7A, 0x37, 0x45, 0xBC, 0x93, 0x61, 0x2F, 0x85, 0xC6, 0x6D, 0xB6, 0xD9, 0xFC, 0xE9, 0x5B, - 0xD9, 0x46, 0xE7, 0xEF, 0xE4, 0xF6, 0xBA, 0x39, 0x0B, 0x7C, 0x76, 0x19, 0xF0, 0x8A, 0x86, 0xAC, - 0xFF, 0x7D, 0xCE, 0xFC, 0xC0, 0x53, 0xEA, 0x73, 0xB0, 0xD9, 0x42, 0xE2, 0xBB, 0x1D, 0xE0, 0xB8, - 0x76, 0xBF, 0x21, 0xA3, 0xA2, 0xAF, 0xDD, 0x43, 0x48, 0xD9, 0xA4, 0x74, 0x92, 0x30, 0x16, 0x29, - 0x10, 0xEA, 0xC2, 0xFC, 0xBC, 0x46, 0xD7, 0xED, 0x74, 0xF7, 0xCE, 0xA7, 0xFA, 0xDD, 0xFA, 0xA8, - 0x25, 0xCA, 0x9B, 0x47, 0x3C, 0xE0, 0x70, 0x39, 0x39, 0x7F, 0xEF, 0xBC, 0x52, 0x78, 0x1C, 0x87, - 0xCA, 0x02, 0x2C, 0xF4, 0x51, 0x4B, 0x34, 0x1F, 0xB5, 0x44, 0x29, 0x94, 0xAA, 0x61, 0x47, 0x7E, - 0x70, 0xA3, 0x4C, 0x42, 0x2F, 0x4D, 0x47, 0x2A, 0x99, 0x16, 0x15, 0x66, 0x63, 0xD5, 0x4C, 0x21, - 0xC0, 0x23, 0x15, 0x21, 0x63, 0x5B, 0x02, 0x1F, 0x98, 0xE4, 0x65, 0x83, 0x45, 0x46, 0xA1, 0x2A, - 0xB3, 0x84, 0x4D, 0x47, 0xEA, 0x8C, 0xF3, 0x45, 0x3A, 0x68, 0xB5, 0xAE, 0x02, 0x3E, 0x5B, 0x8E, - 0x9B, 0x93, 0x78, 0xDE, 0x1A, 0xFB, 0x09, 0xF0, 0xAD, 0xF5, 0x5B, 0x32, 0x0E, 0x2F, 0x4F, 0xD2, - 0x85, 0x63, 0xAB, 0x0A, 0x07, 0x29, 0x66, 0x7C, 0xA4, 0x5E, 0x42, 0x78, 0x1B, 0x7D, 0x01, 0xA8, - 0xE9, 0xCD, 0x55, 0xBE, 0x0E, 0x9B, 0x03, 0x30, 0x62, 0xAD, 0x7C, 0xB8, 0x09, 0xD8, 0xEA, 0x97, - 0xF8, 0x76, 0xA4, 0x62, 0x08, 0x6D, 0x39, 0x26, 0x7C, 0xD9, 0xA6, 0x09, 0xB3, 0xAE, 0x84, 0x57, - 0xC1, 0xAC, 0x7C, 0xA4, 0xD2, 0x2D, 0x68, 0x09, 0xAB, 0xB7, 0x4D, 0x1D, 0x07, 0x68, 0x40, 0x3E, - 0x2F, 0x64, 0x75, 0x4B, 0x57, 0x0C, 0x4B, 0x83, 0xE1, 0x0B, 0x8F, 0xCF, 0x14, 0x7F, 0xA4, 0xBE, - 0xED, 0x20, 0x08, 0xAB, 0xEB, 0x5E, 0x3B, 0x0E, 0x40, 0xEC, 0xBA, 0x8A, 0xD1, 0x0E, 0x9D, 0x1E, - 0x8C, 0x6A, 0xDB, 0x61, 0x1B, 0x2E, 0xD7, 0x6E, 0x1F, 0xBE, 0x5D, 0xA5, 0x0F, 0x3D, 0x4E, 0x1F, - 0x9B, 0xEC, 0xD0, 0x72, 0x5C, 0xA5, 0x67, 0x5E, 0x77, 0x2C, 0xC5, 0x70, 0x7B, 0x8A, 0x65, 0x42, - 0x97, 0x65, 0xB6, 0x43, 0xA3, 0x67, 0xC2, 0x8D, 0xE3, 0x86, 0x0E, 0x00, 0xB9, 0xB6, 0x61, 0xA8, - 0xEB, 0x2A, 0x0E, 0x4C, 0xEF, 0x3B, 0x21, 0x0C, 0xED, 0x84, 0x00, 0x13, 0x80, 0xF4, 0xAE, 0xB1, - 0xC7, 0x51, 0xE0, 0xBB, 0xEB, 0x5C, 0xC3, 0x14, 0x07, 0x17, 0x85, 0x07, 0x37, 0x34, 0xE4, 0x08, - 0xB8, 0x81, 0xF1, 0xD7, 0xF0, 0x08, 0x23, 0xFB, 0xB8, 0x30, 0x01, 0x31, 0x10, 0x70, 0x28, 0x57, - 0xB9, 0xC6, 0xB5, 0x0D, 0xC4, 0xA1, 0x40, 0x80, 0x10, 0xB3, 0x42, 0x84, 0xE6, 0x5C, 0xE3, 0xEA, - 0x06, 0x62, 0x21, 0x51, 0x37, 0x08, 0x77, 0x43, 0x6C, 0xCE, 0x52, 0xAE, 0x11, 0x07, 0xB1, 0x2E, - 0xA2, 0x6B, 0xD0, 0xFE, 0xF1, 0xA1, 0x4D, 0x63, 0x60, 0x08, 0xCE, 0xB0, 0xAF, 0x11, 0x01, 0xD8, - 0x3F, 0x42, 0x11, 0x40, 0x1C, 0xB1, 0x8E, 0xD1, 0xB3, 0xAE, 0x8D, 0x8E, 0xA9, 0x20, 0x16, 0x88, - 0x01, 0x22, 0xD0, 0x43, 0x9E, 0xB8, 0x88, 0x27, 0x00, 0x84, 0xA5, 0x5D, 0x44, 0xA4, 0xA7, 0x20, - 0xEA, 0xB6, 0xD2, 0x09, 0x69, 0x5D, 0xD8, 0xBF, 0xD1, 0x51, 0x5C, 0xD8, 0x67, 0x07, 0xC8, 0x0D, - 0xFB, 0x87, 0x85, 0xE1, 0x0E, 0x48, 0x44, 0x9D, 0x21, 0x0C, 0xBC, 0xB6, 0x1C, 0x04, 0x2B, 0x66, - 0x3A, 0x8A, 0xA0, 0x2C, 0x6E, 0xD9, 0xED, 0x2A, 0xB0, 0x61, 0x58, 0x89, 0x56, 0xB3, 0x60, 0x26, - 0xF4, 0x84, 0x88, 0x25, 0xAC, 0x04, 0xEB, 0x09, 0x1C, 0xA1, 0x37, 0xA4, 0x1D, 0x40, 0x33, 0x92, - 0x19, 0xF7, 0xF4, 0x95, 0x18, 0xDD, 0x03, 0x82, 0x5E, 0x1B, 0xBD, 0x3E, 0xEE, 0x94, 0x48, 0xDD, - 0x71, 0x38, 0x7C, 0x88, 0x20, 0xCD, 0x36, 0x2F, 0xEE, 0xB2, 0x4E, 0xBC, 0xC2, 0x05, 0x3A, 0x44, - 0xBB, 0x51, 0xDC, 0x89, 0xAE, 0xAF, 0x20, 0x4B, 0x2D, 0x14, 0x26, 0xB8, 0x5C, 0xC1, 0x07, 0x84, - 0xF7, 0x58, 0x39, 0x82, 0x70, 0x26, 0xCA, 0x75, 0x22, 0xCB, 0xDA, 0xD4, 0xE3, 0xD7, 0x41, 0x32, - 0x5F, 0x41, 0xD8, 0x03, 0xC3, 0x60, 0x00, 0x8C, 0xF6, 0xE0, 0x83, 0x0A, 0xF4, 0x08, 0x25, 0x5A, - 0xAD, 0x56, 0xCD, 0x92, 0x22, 0x85, 0xCB, 0x89, 0x21, 0x1E, 0x5B, 0xA4, 0xD1, 0xC6, 0xC7, 0x93, - 0x5F, 0x3E, 0x9C, 0xB6, 0x38, 0xD8, 0x88, 0x96, 0xDD, 0xB4, 0xFE, 0x1A, 0x6A, 0x65, 0xF6, 0xDD, - 0xEB, 0x9E, 0x8D, 0x10, 0x3B, 0x66, 0x13, 0xA5, 0xCF, 0x46, 0xD2, 0xBA, 0x40, 0xFC, 0x76, 0x9F, - 0x5B, 0x56, 0x07, 0xDB, 0x7A, 0xD8, 0xD6, 0x77, 0xF1, 0xB6, 0x0F, 0x1C, 0xE8, 0xD1, 0xC5, 0xB5, - 0xF3, 0x2E, 0x14, 0xBD, 0x76, 0x97, 0x08, 0x9E, 0xDF, 0xA1, 0xE0, 0x52, 0xA7, 0xD1, 0xE9, 0xC9, - 0x89, 0x46, 0x0E, 0xC2, 0x28, 0x03, 0x36, 0xB2, 0xD5, 0x80, 0x5D, 0xFD, 0x1C, 0x05, 0xF9, 0x60, - 0xE7, 0x23, 0x68, 0x00, 0x4D, 0x13, 0xB3, 0x08, 0x58, 0x3F, 0x83, 0xDF, 0x17, 0x4B, 0x66, 0x00, - 0x15, 0x42, 0x22, 0xBB, 0x12, 0xAA, 0xD4, 0x05, 0xB8, 0xF7, 0xDB, 0x0A, 0xCF, 0xE6, 0x96, 0xE0, - 0xC9, 0x25, 0x04, 0x15, 0x70, 0xD5, 0xAF, 0x6F, 0x7B, 0xBD, 0x1E, 0xF4, 0xF5, 0x49, 0xC5, 0x51, - 0xCB, 0x2D, 0x90, 0x57, 0x9B, 0x13, 0x82, 0x64, 0x39, 0xDA, 0x5D, 0x94, 0x67, 0x40, 0xAA, 0x8F, - 0x16, 0xC2, 0xB2, 0x51, 0xDF, 0x80, 0x36, 0x36, 0x0C, 0xC2, 0x2F, 0x7C, 0x12, 0x37, 0x78, 0x85, - 0x1E, 0xB8, 0xBD, 0xC6, 0x45, 0x14, 0x1B, 0x04, 0xD4, 0x02, 0xB2, 0x2B, 0x56, 0x5F, 0x71, 0x69, - 0x39, 0xC0, 0xB9, 0x8B, 0x5B, 0x87, 0x11, 0x46, 0x17, 0x80, 0x75, 0xD0, 0xA0, 0x75, 0x10, 0x6A, - 0x0F, 0x8C, 0x88, 0x85, 0x32, 0xDF, 0x51, 0x84, 0xA9, 0x31, 0x91, 0x17, 0x70, 0x05, 0x14, 0xAF, - 0x6D, 0xB4, 0x44, 0xA0, 0xA8, 0x5D, 0x30, 0x0A, 0x16, 0xC7, 0x89, 0x3D, 0x9B, 0xF7, 0x05, 0x63, - 0x2C, 0xD8, 0x1D, 0x1A, 0x8F, 0x1E, 0x6E, 0xCE, 0x71, 0x88, 0xB0, 0xB8, 0x98, 0x7C, 0xB0, 0x5D, - 0xEA, 0xA7, 0x6E, 0x9A, 0xD1, 0x43, 0x8D, 0xE9, 0x9A, 0xE2, 0x0A, 0x10, 0xBB, 0xB0, 0xD0, 0xB5, - 0x05, 0x9A, 0x0C, 0x24, 0x53, 0xDC, 0x8C, 0xAE, 0x2E, 0xF4, 0x5E, 0x1B, 0x7D, 0xB2, 0xC7, 0x88, - 0x14, 0xEC, 0xA4, 0xD7, 0xFF, 0xFA, 0xD6, 0x05, 0x53, 0xD0, 0xB5, 0xBB, 0x60, 0x55, 0xD0, 0x9A, - 0x48, 0xAB, 0x48, 0x1F, 0xE2, 0xA8, 0x83, 0xAB, 0x10, 0xF3, 0x69, 0xBE, 0x03, 0x53, 0x91, 0x13, - 0xB8, 0x2D, 0x0B, 0x2C, 0x09, 0x6E, 0xCD, 0x51, 0x1C, 0x92, 0x13, 0xCB, 0xE2, 0x0E, 0x32, 0xC5, - 0xEA, 0x86, 0x00, 0x0B, 0xEC, 0x09, 0x2C, 0x8A, 0xF4, 0x47, 0x14, 0x11, 0x71, 0xC0, 0xA2, 0x23, - 0x6F, 0xC9, 0x7A, 0xA2, 0x01, 0x05, 0x63, 0x01, 0xE8, 0xC0, 0xA2, 0x84, 0xAD, 0x61, 0x03, 0xA9, - 0x4D, 0x6E, 0x38, 0x36, 0xD2, 0xF3, 0x49, 0xCA, 0x7F, 0x8A, 0x19, 0xD2, 0x14, 0xD2, 0x9C, 0xE7, - 0x68, 0xFF, 0x21, 0x17, 0xDA, 0x5A, 0x05, 0x5F, 0x82, 0xBF, 0x86, 0xC2, 0x5B, 0xDD, 0xEE, 0x35, - 0x32, 0xCF, 0x04, 0xB1, 0x03, 0xDA, 0xB9, 0x6D, 0x94, 0x8F, 0x9E, 0x2B, 0xA4, 0x0F, 0xAC, 0xA9, - 0xED, 0x90, 0xD4, 0x21, 0xC3, 0xDA, 0x42, 0x1B, 0x5D, 0x6E, 0x94, 0x6E, 0x4B, 0x03, 0x8C, 0xD2, - 0x3C, 0xA3, 0x80, 0x46, 0xB7, 0xE2, 0x4E, 0x0C, 0xA0, 0x7E, 0x9C, 0x27, 0xA7, 0x11, 0x34, 0x04, - 0x96, 0xDF, 0x14, 0x9D, 0xC5, 0x8C, 0x0C, 0xCA, 0xD7, 0xB7, 0x6D, 0x50, 0x9E, 0xBE, 0x0B, 0xAE, - 0xCC, 0x26, 0xAF, 0x00, 0x1A, 0x64, 0xB4, 0xA5, 0xA1, 0x37, 0x6C, 0xD4, 0x07, 0x90, 0x72, 0x29, - 0x64, 0x24, 0x60, 0xC2, 0x67, 0x48, 0x13, 0x83, 0x02, 0x88, 0xFA, 0x09, 0xAA, 0x6A, 0xD3, 0x65, - 0x66, 0x39, 0xD6, 0xB5, 0x83, 0x70, 0x14, 0x90, 0x30, 0xCB, 0xBA, 0xEE, 0x60, 0x87, 0x4D, 0x7A, - 0xDF, 0x13, 0x28, 0xF5, 0xAE, 0x6D, 0xA4, 0xB9, 0x43, 0xB0, 0x2C, 0x5C, 0xC1, 0xA2, 0x5B, 0x1B, - 0x96, 0x20, 0x58, 0xB0, 0x70, 0x17, 0x83, 0x02, 0xD8, 0x2B, 0x0A, 0x2F, 0xB8, 0x4B, 0x8B, 0xFC, - 0x15, 0xA9, 0x19, 0x12, 0x89, 0xC4, 0x9E, 0x0C, 0x21, 0x2E, 0x4A, 0x20, 0x0C, 0xD4, 0x56, 0xAB, - 0x8B, 0x44, 0x11, 0xCA, 0x88, 0x36, 0x8E, 0xB4, 0x03, 0xFA, 0x00, 0x7F, 0x17, 0xD5, 0x06, 0x10, - 0x56, 0xA8, 0x11, 0xB1, 0xE7, 0x84, 0x94, 0x01, 0xC3, 0x67, 0x16, 0x84, 0x21, 0x82, 0x67, 0x4A, - 0x8F, 0x3B, 0x84, 0xA9, 0x83, 0x16, 0xC3, 0xED, 0x71, 0x1B, 0xD7, 0xEA, 0x22, 0x0D, 0xC1, 0x9A, - 0x9B, 0x80, 0x1E, 0x9A, 0x03, 0x60, 0x70, 0xCF, 0x51, 0x38, 0x99, 0x09, 0x30, 0x82, 0x40, 0x21, - 0x1C, 0xE5, 0x10, 0xF9, 0x3B, 0xA0, 0x51, 0x3D, 0x6C, 0x41, 0xE3, 0x03, 0x4E, 0xB7, 0x0B, 0x60, - 0x4C, 0x73, 0x06, 0xD8, 0x98, 0x80, 0x81, 0x49, 0x1B, 0xE9, 0xE6, 0xF8, 0x0B, 0xBB, 0x04, 0xDF, - 0x37, 0x34, 0x80, 0x76, 0xA3, 0xE4, 0x8D, 0x3C, 0x1F, 0x39, 0xC3, 0x5E, 0x9A, 0x4D, 0x4D, 0xD8, - 0xD7, 0x25, 0x3D, 0x86, 0x99, 0x62, 0xA2, 0x65, 0xD2, 0x40, 0x6A, 0x92, 0x06, 0x0F, 0x3E, 0x4F, - 0x52, 0xD0, 0xDF, 0x59, 0xB8, 0xD8, 0xA1, 0x9B, 0x0A, 0x85, 0xCF, 0x23, 0xB5, 0x88, 0xA9, 0xD5, - 0xA2, 0x2F, 0x8E, 0x26, 0x61, 0x30, 0xF9, 0x32, 0x52, 0xCF, 0xDE, 0xD4, 0xB5, 0xA1, 0xAA, 0x04, - 0xA0, 0x0A, 0x61, 0x0C, 0x59, 0x59, 0x00, 0xA1, 0xB8, 0xBA, 0xAD, 0xD6, 0x55, 0xAD, 0x6C, 0x3A, - 0x15, 0xBD, 0x6C, 0xDA, 0xDF, 0x5D, 0x33, 0xA7, 0x41, 0x18, 0xCA, 0x4D, 0xAA, 0xA4, 0xA6, 0x7D, - 0x8C, 0x82, 0x4C, 0xF3, 0xC6, 0x26, 0x6E, 0xF6, 0xA4, 0x11, 0x87, 0x00, 0x4F, 0xC4, 0x34, 0x24, - 0xE1, 0xD8, 0x32, 0x33, 0x60, 0x65, 0x08, 0x95, 0x6C, 0xE2, 0x98, 0x6B, 0x09, 0x27, 0xDA, 0xA6, - 0x58, 0xD8, 0xBA, 0x01, 0xC1, 0x43, 0x9E, 0xE2, 0x08, 0x97, 0x84, 0xB3, 0x2B, 0xFC, 0x7B, 0x9F, - 0x44, 0x51, 0xC8, 0xA7, 0x89, 0x5C, 0xED, 0xD0, 0x32, 0x38, 0xA8, 0x68, 0xE5, 0xC5, 0xE0, 0x19, - 0xA0, 0x73, 0x4D, 0x10, 0xA8, 0x8D, 0xE6, 0x0B, 0x91, 0xC3, 0xD9, 0x62, 0x32, 0xAE, 0x9D, 0xB7, - 0x71, 0x23, 0x1F, 0x48, 0xEB, 0x83, 0x77, 0x90, 0x1B, 0x12, 0x62, 0x60, 0x5B, 0x68, 0xD0, 0x1D, - 0xD4, 0x60, 0xD4, 0x3F, 0x90, 0xC1, 0x19, 0xA0, 0xAA, 0x48, 0x95, 0x43, 0xB9, 0x22, 0x3B, 0x40, - 0x8A, 0x21, 0xA5, 0x8E, 0x76, 0xFA, 0x55, 0x6D, 0x95, 0x44, 0x64, 0x17, 0xFB, 0x4B, 0x95, 0x34, - 0xCA, 0xF6, 0x90, 0x95, 0x28, 0x47, 0xC8, 0xF1, 0xD7, 0x1F, 0xFF, 0xF3, 0xE4, 0xEC, 0xFC, 0xF4, - 0xDD, 0x1F, 0xEA, 0x0E, 0xB1, 0xCA, 0x45, 0x0A, 0xE1, 0xB5, 0x30, 0x65, 0x6A, 0x89, 0x43, 0x0E, - 0x47, 0x2D, 0x48, 0xB3, 0x76, 0xE6, 0x5A, 0xA2, 0x54, 0x77, 0x7C, 0x34, 0xB3, 0x09, 0xFC, 0xDB, - 0xF3, 0xDF, 0x10, 0xCC, 0xCC, 0x86, 0xAF, 0xAC, 0x6B, 0xF7, 0x5C, 0x45, 0x66, 0x9D, 0x42, 0x10, - 0x5F, 0x9F, 0xBE, 0x39, 0x39, 0xFF, 0xDB, 0xF9, 0xC5, 0xC9, 0x5B, 0x75, 0x7B, 0x68, 0xF6, 0x66, - 0x1D, 0xA2, 0x52, 0x68, 0x9D, 0x29, 0xAF, 0x83, 0x90, 0xA5, 0x77, 0x29, 0x67, 0xF3, 0x3D, 0xB0, - 0x29, 0x33, 0x07, 0x40, 0x54, 0xBA, 0x54, 0xA8, 0x74, 0xA9, 0x62, 0xB1, 0x52, 0xAC, 0x45, 0x65, - 0x4B, 0x51, 0x3F, 0x53, 0x95, 0xC8, 0x9B, 0x43, 0xE7, 0xFC, 0x0E, 0x1B, 0xD3, 0x4F, 0x9F, 0x55, - 0x65, 0xBE, 0x0C, 0x79, 0xB0, 0x40, 0x32, 0x66, 0x77, 0x2A, 0xE8, 0xA1, 0x80, 0x54, 0x28, 0x88, - 0x52, 0x7A, 0x31, 0xA6, 0xCA, 0x15, 0x44, 0x09, 0x54, 0xAC, 0x51, 0xA9, 0x8A, 0xAA, 0x85, 0xEE, - 0x9D, 0xB3, 0xC8, 0xC7, 0xA5, 0x48, 0x03, 0x6F, 0xBC, 0x70, 0x09, 0xF3, 0x3E, 0xD0, 0x58, 0xF5, - 0xF8, 0x45, 0x34, 0x4E, 0x17, 0x43, 0xF1, 0x7D, 0xB4, 0x48, 0xE2, 0xAB, 0x84, 0xA5, 0x69, 0xC6, - 0xD3, 0x9B, 0x20, 0x0D, 0xC6, 0x41, 0x18, 0xF0, 0xBB, 0x01, 0x10, 0xCE, 0x67, 0x51, 0x86, 0xFA, - 0x22, 0xB9, 0x12, 0x4B, 0xD2, 0x0D, 0x64, 0xDB, 0x94, 0xF2, 0x92, 0x31, 0x91, 0x20, 0x20, 0x53, - 0x4E, 0xC4, 0x67, 0x07, 0xFF, 0xF6, 0x91, 0x4E, 0xF2, 0x5D, 0xA4, 0xCD, 0x99, 0x15, 0x20, 0x7B, - 0xF2, 0x14, 0x52, 0x54, 0xF6, 0xFD, 0x6B, 0x3C, 0x9F, 0x7B, 0x91, 0x5F, 0xAF, 0x85, 0x41, 0xCA, - 0x6B, 0x7A, 0xCD, 0x0B, 0xC3, 0x5A, 0x89, 0x0C, 0x67, 0x6C, 0x0A, 0xD8, 0xCE, 0x4A, 0x16, 0xAB, - 0xBC, 0x2A, 0xE2, 0x99, 0x43, 0xFB, 0x35, 0x61, 0x60, 0x4E, 0xFC, 0x20, 0xA9, 0x6B, 0xEA, 0x21, - 0xAB, 0xE5, 0x9A, 0x85, 0xC9, 0xC2, 0xFB, 0x8A, 0xBD, 0x72, 0xF1, 0x7F, 0x18, 0x9F, 0x80, 0x1C, - 0x28, 0xD0, 0xD6, 0x56, 0x95, 0x3B, 0xA4, 0x9D, 0x9A, 0xCD, 0x76, 0x4A, 0xB3, 0x6D, 0xB8, 0x4F, - 0x60, 0x90, 0x0D, 0x97, 0x3B, 0xBA, 0x08, 0x73, 0x25, 0xCB, 0xAB, 0xA8, 0x92, 0x19, 0x1C, 0x1C, - 0x7A, 0x47, 0xE0, 0x32, 0xDB, 0xD9, 0x2E, 0x19, 0xCE, 0xF6, 0x83, 0x70, 0x50, 0x7B, 0x11, 0x8E, - 0x25, 0x10, 0xB2, 0xE1, 0x92, 0x17, 0x95, 0xA1, 0xB5, 0x27, 0x1F, 0x57, 0x12, 0x22, 0x18, 0x94, - 0x0C, 0x08, 0xD5, 0xA7, 0xD5, 0xE3, 0x06, 0x10, 0x10, 0x60, 0xE4, 0x06, 0x82, 0x54, 0x64, 0x83, - 0xA6, 0xD2, 0x37, 0x20, 0x55, 0x49, 0x76, 0xC0, 0x00, 0xE7, 0x94, 0x0C, 0xA2, 0x69, 0x9C, 0x49, - 0x63, 0x79, 0x76, 0xC5, 0x20, 0x88, 0x1A, 0x8B, 0x9C, 0x21, 0x1E, 0x2A, 0x87, 0x95, 0xD4, 0x4C, - 0x70, 0x8B, 0x8A, 0x3A, 0x4A, 0x95, 0xA8, 0xDC, 0x90, 0x54, 0xCD, 0xCA, 0xFC, 0xBD, 0x00, 0xB1, - 0x01, 0xD8, 0x33, 0x6C, 0x3F, 0xFE, 0x03, 0x04, 0x3B, 0x7F, 0x38, 0x87, 0x6D, 0x67, 0x0F, 0xC2, - 0x54, 0x9C, 0x5F, 0xF2, 0x60, 0x0E, 0xFB, 0xBC, 0x08, 0x8A, 0x61, 0x15, 0x59, 0xD9, 0x68, 0xCB, - 0xFD, 0xE0, 0x2C, 0xDF, 0x83, 0x44, 0x03, 0x65, 0x3D, 0x37, 0x09, 0x97, 0x28, 0x96, 0x34, 0x4E, - 0x14, 0x96, 0x0E, 0x9B, 0x3E, 0x59, 0xDE, 0x13, 0xAA, 0x97, 0x72, 0x8F, 0x2F, 0x53, 0x35, 0xA7, - 0xF5, 0xD6, 0xF7, 0x03, 0xC6, 0xEF, 0xE3, 0x87, 0xF7, 0xAF, 0x7E, 0xBE, 0x38, 0x39, 0x6C, 0xFA, - 0x64, 0x42, 0xAE, 0x7C, 0x58, 0xF8, 0x20, 0xFC, 0x0F, 0x58, 0xBE, 0x8A, 0xFA, 0xEE, 0x35, 0x84, - 0xAB, 0xBD, 0x66, 0xB0, 0x14, 0xEE, 0x3F, 0xD9, 0xF4, 0xC1, 0x43, 0x49, 0xF3, 0x85, 0x75, 0xDB, - 0xB6, 0x79, 0xB8, 0x89, 0xF2, 0x32, 0x4F, 0x31, 0x78, 0xD3, 0x55, 0x6E, 0xF2, 0xF0, 0x76, 0xB7, - 0xD1, 0xCB, 0x21, 0xE7, 0xBE, 0x6F, 0x9E, 0x5E, 0xA9, 0xFB, 0xC1, 0x1F, 0x9F, 0x31, 0xE0, 0x63, - 0xC2, 0x81, 0xDA, 0xBA, 0x02, 0x66, 0xDF, 0x4B, 0x99, 0xB2, 0xF2, 0x02, 0xDE, 0x84, 0xFF, 0x32, - 0xC7, 0x98, 0x83, 0x9A, 0xC4, 0x4B, 0x74, 0x6E, 0x0F, 0xBB, 0xCC, 0x82, 0x4D, 0x79, 0xBC, 0x85, - 0xB5, 0xCC, 0x5C, 0xD9, 0xA8, 0x18, 0x5A, 0x65, 0x7C, 0xA5, 0x3E, 0xBA, 0xAB, 0x4B, 0x94, 0xC5, - 0xA1, 0x67, 0xE6, 0x1C, 0x9F, 0x02, 0xEA, 0x3C, 0x98, 0x06, 0x13, 0x7A, 0xC9, 0x05, 0x9E, 0xD7, - 0xD9, 0x21, 0x73, 0x45, 0xB9, 0x5A, 0x86, 0x02, 0xC7, 0x95, 0xC0, 0x52, 0x74, 0xA3, 0xCD, 0x50, - 0x95, 0x3C, 0x58, 0x3B, 0xFE, 0x90, 0x82, 0xDA, 0xCA, 0xED, 0x6D, 0x38, 0xC0, 0xF2, 0x99, 0xA6, - 0x4C, 0x04, 0xC4, 0x74, 0xDA, 0x24, 0xF2, 0xBF, 0x12, 0x89, 0x62, 0xD9, 0xBF, 0x20, 0xD5, 0x2C, - 0x79, 0x3C, 0x12, 0xEF, 0xA1, 0x6F, 0x05, 0x16, 0xE4, 0x09, 0x88, 0x2C, 0xE4, 0x14, 0x89, 0xCC, - 0xE2, 0x30, 0x32, 0xE3, 0x5D, 0x01, 0x4A, 0xB9, 0x80, 0xBF, 0x11, 0x46, 0x64, 0x92, 0xBE, 0x4F, - 0x1F, 0x0A, 0x8F, 0xF7, 0xE6, 0x0C, 0xBD, 0x93, 0x94, 0xF7, 0xF3, 0xE5, 0x78, 0x1E, 0xF0, 0x9D, - 0x16, 0x22, 0x9D, 0x80, 0xC1, 0xE4, 0xC7, 0x37, 0x5E, 0xA2, 0xAC, 0xD2, 0xCB, 0x34, 0x5E, 0x26, - 0x13, 0xA6, 0xDF, 0xCE, 0x43, 0xCC, 0xA7, 0x45, 0x18, 0xA1, 0x4F, 0x96, 0x09, 0xBE, 0x80, 0x44, - 0x2B, 0x3D, 0x52, 0x5B, 0xAA, 0x0E, 0x5B, 0x98, 0x21, 0xDB, 0x05, 0xD3, 0x47, 0x3F, 0x58, 0xFA, - 0x8A, 0x8D, 0xD3, 0x78, 0xF2, 0x85, 0xF1, 0xCB, 0x45, 0x9C, 0xF0, 0x91, 0x59, 0x6A, 0x38, 0x7D, - 0x3F, 0x52, 0x61, 0x4A, 0x7A, 0x17, 0x4D, 0x2E, 0xA1, 0x15, 0xF2, 0xF2, 0xF9, 0x32, 0x2A, 0x4D, - 0x45, 0x71, 0xBC, 0x44, 0x52, 0xA9, 0x3A, 0x88, 0xE7, 0x65, 0x3C, 0x9D, 0x56, 0x01, 0x92, 0x52, - 0x30, 0x1F, 0x1B, 0x59, 0xBA, 0xB8, 0x64, 0x49, 0x12, 0x27, 0x97, 0x73, 0x50, 0x31, 0x98, 0x87, - 0x93, 0x8A, 0xC6, 0x49, 0xEC, 0x33, 0x58, 0x1A, 0x09, 0x25, 0x10, 0x1F, 0x99, 0xC3, 0xE9, 0x32, - 0xA2, 0x77, 0xB1, 0xA0, 0xBC, 0x37, 0x63, 0x0F, 0x1C, 0xF6, 0x3D, 0x6E, 0x14, 0x26, 0x96, 0x4C, - 0x94, 0xAA, 0xF3, 0x51, 0x69, 0x8B, 0xCD, 0x74, 0x01, 0x7A, 0x59, 0x87, 0x8D, 0x6A, 0x7A, 0x44, - 0xFB, 0x0D, 0x46, 0xD6, 0x10, 0x58, 0x5D, 0x67, 0x0D, 0x9C, 0xE7, 0x4B, 0xE2, 0xD7, 0x84, 0xCB, - 0xAF, 0x29, 0x39, 0xD1, 0xFF, 0x54, 0xCB, 0x94, 0xAA, 0xB5, 0x6A, 0x43, 0x65, 0x7F, 0xE0, 0xF1, - 0xA7, 0x7A, 0xDC, 0x22, 0xB5, 0x55, 0x87, 0xC1, 0x11, 0x6F, 0x86, 0x2C, 0xBA, 0xE2, 0x10, 0xA8, - 0x0F, 0xB5, 0x3D, 0xAB, 0xEC, 0x59, 0x44, 0x6D, 0xD4, 0xA3, 0xC6, 0x88, 0x7F, 0x0A, 0x3E, 0x37, - 0x10, 0xE3, 0x86, 0xFA, 0xD0, 0xA2, 0x6A, 0x43, 0x0C, 0xCE, 0x0D, 0x94, 0xC4, 0x42, 0x0F, 0x1A, - 0x8D, 0x61, 0xC2, 0xF8, 0x32, 0x89, 0x14, 0x42, 0xA1, 0x6C, 0x4D, 0xD4, 0x75, 0x4E, 0x48, 0x50, - 0x8E, 0x74, 0x76, 0x89, 0x49, 0x1B, 0x10, 0x53, 0x8C, 0x57, 0xB3, 0xE0, 0xA3, 0xD6, 0xB6, 0x6B, - 0x10, 0x34, 0xD4, 0x2C, 0xB8, 0x40, 0x98, 0x51, 0xEB, 0xD4, 0x30, 0xCC, 0xC0, 0x8B, 0xF0, 0x85, - 0x35, 0xBB, 0x5D, 0xCB, 0x62, 0x91, 0x5A, 0xB7, 0x26, 0x15, 0xA3, 0x86, 0xE1, 0xC3, 0x20, 0x61, - 0xFE, 0xB0, 0xA6, 0xB4, 0x00, 0x91, 0x6D, 0x70, 0xBB, 0x01, 0xD8, 0x55, 0x00, 0x14, 0x7E, 0x6C, - 0x81, 0x70, 0x4C, 0x01, 0xA2, 0xB7, 0x07, 0xA3, 0x4E, 0xB7, 0x00, 0x08, 0x36, 0xFC, 0x61, 0x9C, - 0xEC, 0x2A, 0x40, 0xCB, 0x14, 0x10, 0xF1, 0x2A, 0x41, 0xF6, 0xCA, 0x20, 0xDD, 0x47, 0x43, 0xB4, - 0xFB, 0x3B, 0x21, 0x38, 0x8F, 0xD9, 0xA5, 0x2B, 0x40, 0xB8, 0x8E, 0x40, 0xAA, 0x2B, 0x70, 0xEA, - 0xE6, 0x00, 0x4B, 0xF0, 0x3A, 0x8F, 0x02, 0xD8, 0xF9, 0xDE, 0x00, 0x7B, 0xDF, 0x03, 0xA0, 0x08, - 0x29, 0x11, 0x6C, 0x11, 0x65, 0xD7, 0x6C, 0xB7, 0x24, 0x12, 0x70, 0x9F, 0x45, 0xD9, 0x35, 0xAA, - 0x0A, 0xD8, 0x58, 0x2D, 0xEE, 0xD5, 0x8E, 0xBF, 0xA7, 0x88, 0xFE, 0xBD, 0xF2, 0xF9, 0x7D, 0x85, - 0xF3, 0x3B, 0x4B, 0xE6, 0xDF, 0x2B, 0x96, 0xDF, 0x57, 0x26, 0xBF, 0xAF, 0x40, 0xFE, 0x43, 0xA4, - 0xB1, 0x30, 0x8D, 0xF8, 0xFA, 0x7C, 0xD3, 0x32, 0x3E, 0x56, 0x50, 0x6D, 0x17, 0xFE, 0xAF, 0xE5, - 0x55, 0xE2, 0xDA, 0xDB, 0xAE, 0xEE, 0x28, 0x6F, 0x6C, 0xBD, 0xA7, 0xBC, 0xE9, 0xEA, 0x96, 0x43, - 0xDF, 0xA6, 0xF2, 0xC6, 0x92, 0x97, 0x9E, 0x6E, 0x59, 0xE2, 0xD2, 0x16, 0x8D, 0x1D, 0xB8, 0x98, - 0x74, 0xE9, 0xEB, 0x56, 0x97, 0xBE, 0xFB, 0xD4, 0x64, 0xC3, 0x70, 0x5B, 0x5E, 0x6C, 0xDD, 0xEA, - 0xD1, 0xA5, 0x47, 0x6D, 0x1D, 0x84, 0xDA, 0x51, 0xBE, 0xE2, 0x06, 0x93, 0xF8, 0x0B, 0xEC, 0x90, - 0x8A, 0x31, 0x35, 0x91, 0xCF, 0xD5, 0x68, 0xA7, 0x3B, 0x37, 0x2A, 0xC2, 0xF6, 0x4B, 0xCC, 0x80, - 0x99, 0x76, 0x5F, 0xF2, 0x47, 0x8D, 0x11, 0x43, 0x37, 0xA4, 0x97, 0x3D, 0x90, 0x4A, 0x89, 0x8D, - 0xAE, 0x82, 0x07, 0x52, 0xB5, 0x02, 0x06, 0x04, 0x02, 0x78, 0xAE, 0xE9, 0x9C, 0x63, 0x8D, 0x3E, - 0xAD, 0x33, 0x9D, 0x67, 0x44, 0xAB, 0xB3, 0x11, 0x6B, 0xF2, 0xF8, 0x4D, 0xBC, 0x62, 0xC9, 0xAF, - 0x10, 0x06, 0xD7, 0x35, 0xED, 0xA8, 0xCE, 0x47, 0x7C, 0xA3, 0xED, 0xA5, 0x61, 0x0D, 0xF8, 0x11, - 0x7B, 0x69, 0x0D, 0xCC, 0x02, 0x2A, 0x9E, 0x21, 0xF0, 0xF8, 0x64, 0x46, 0x89, 0x04, 0xA5, 0x45, - 0x88, 0x21, 0xFA, 0x7C, 0x8E, 0xC1, 0x02, 0x46, 0x1C, 0xC3, 0x60, 0x0A, 0xD0, 0xD4, 0x72, 0xB9, - 0xE3, 0x9C, 0x46, 0x0E, 0x14, 0xB5, 0xC1, 0x9A, 0x62, 0x96, 0xCE, 0x1B, 0xD5, 0x21, 0xDF, 0xCA, - 0x0F, 0x17, 0x31, 0xF7, 0x42, 0x45, 0x9C, 0xC4, 0xA2, 0x49, 0x1C, 0x1B, 0x0E, 0xCF, 0x81, 0x78, - 0xD6, 0x2F, 0x4F, 0x59, 0xC2, 0xF3, 0xE1, 0x19, 0xEF, 0x26, 0x93, 0xE5, 0x42, 0xFC, 0x06, 0x46, - 0x51, 0x69, 0xE8, 0xD1, 0x9C, 0x41, 0x48, 0xA8, 0xCC, 0x83, 0x08, 0x84, 0xA6, 0x46, 0x89, 0x87, - 0xB0, 0x0B, 0x33, 0x90, 0xAA, 0x51, 0xAD, 0x0F, 0x77, 0x22, 0xE0, 0xAB, 0xE1, 0x0A, 0x71, 0x3E, - 0x1F, 0xA2, 0x02, 0x60, 0x22, 0x4D, 0x96, 0x89, 0xF5, 0x66, 0xFF, 0x4F, 0xAA, 0xEE, 0xC7, 0x93, - 0xE5, 0x1C, 0xF8, 0xD8, 0xBC, 0x62, 0xFC, 0x24, 0x64, 0x78, 0xFB, 0xCB, 0xDD, 0x29, 0xF0, 0x4F, - 0xA6, 0x97, 0x5A, 0x33, 0x88, 0x22, 0x96, 0xFC, 0x7E, 0xF1, 0xF6, 0xCD, 0x88, 0xEB, 0x44, 0x4E, - 0x60, 0xF5, 0x0F, 0xE5, 0xD0, 0x49, 0x50, 0x3A, 0xA8, 0x44, 0x53, 0x10, 0xC5, 0xF0, 0x53, 0x3C, - 0xDD, 0xF4, 0x6E, 0x8A, 0x31, 0x95, 0x5E, 0xE9, 0x13, 0x41, 0x8F, 0xAD, 0x0D, 0x69, 0x77, 0x3C, - 0xC9, 0x34, 0xAD, 0x7C, 0x5A, 0xF9, 0x40, 0xE8, 0x53, 0x89, 0xDA, 0x60, 0x08, 0xAB, 0x9B, 0x10, - 0xC6, 0x58, 0x8F, 0x88, 0x82, 0x30, 0xC0, 0x82, 0x50, 0xA8, 0xA4, 0xB1, 0x45, 0x44, 0x04, 0xA2, - 0x19, 0x62, 0x74, 0x0E, 0xF6, 0xA1, 0x76, 0x0C, 0xF9, 0x2E, 0xE6, 0x5F, 0x59, 0x7A, 0xA5, 0xAE, - 0x59, 0x93, 0x64, 0xAB, 0x09, 0xF8, 0xF1, 0x7A, 0x26, 0x77, 0x65, 0xF1, 0xDD, 0x92, 0xEC, 0x26, - 0x26, 0x8E, 0x3A, 0xA7, 0x8B, 0xB6, 0xD6, 0x90, 0x6A, 0xA3, 0x32, 0x89, 0x5E, 0xBC, 0xA8, 0x83, - 0x5C, 0x9A, 0x1A, 0xC5, 0x98, 0x48, 0xC0, 0x10, 0x83, 0xDD, 0x18, 0xA2, 0xD7, 0xF8, 0x28, 0x5B, - 0x4D, 0x50, 0x6A, 0x18, 0x37, 0x1A, 0x9A, 0x6A, 0x58, 0x40, 0x75, 0x01, 0xBD, 0x2E, 0xFB, 0x3F, - 0xC5, 0x9F, 0x9B, 0x58, 0xA0, 0xD1, 0x00, 0x16, 0x91, 0xF2, 0xE2, 0xEC, 0x58, 0x8A, 0x0C, 0xA5, - 0xA0, 0x60, 0x84, 0xCA, 0x96, 0xA7, 0x64, 0x90, 0x76, 0x18, 0x21, 0xE5, 0x58, 0x81, 0xFF, 0x0A, - 0x4B, 0x64, 0xE9, 0x36, 0x58, 0x12, 0xDD, 0xB6, 0xD0, 0x1E, 0xD9, 0x78, 0xDF, 0x11, 0x97, 0x2E, - 0xB5, 0x59, 0x68, 0x43, 0xDE, 0x58, 0xB6, 0xFC, 0xB6, 0x14, 0x1C, 0x66, 0x3D, 0xC2, 0xAA, 0xE0, - 0xE1, 0x44, 0xE5, 0xD6, 0x12, 0xBE, 0xF8, 0x0E, 0xAF, 0x35, 0xE5, 0xD6, 0x86, 0x0B, 0x58, 0xDF, - 0x3B, 0x9B, 0xFC, 0xE0, 0x06, 0x04, 0xF1, 0x68, 0x48, 0xF4, 0xAD, 0x5A, 0x2B, 0xDB, 0xA4, 0x2C, - 0x35, 0x51, 0x1C, 0x0B, 0x0D, 0xB5, 0xA3, 0x8B, 0x57, 0x32, 0x7E, 0xFE, 0x53, 0x06, 0xD0, 0x7F, - 0x66, 0x56, 0x5C, 0xCD, 0xCF, 0x7F, 0x2D, 0x6E, 0x87, 0xF4, 0xDE, 0x41, 0xBC, 0x3F, 0xAC, 0x81, - 0x46, 0x90, 0x09, 0x2B, 0x91, 0x14, 0x39, 0xD6, 0xA8, 0xE5, 0x2F, 0x0C, 0xC5, 0xFB, 0xC2, 0x4A, - 0xAA, 0x56, 0xAA, 0x5E, 0xD7, 0x70, 0xE5, 0x8D, 0xC9, 0xBA, 0x4A, 0x07, 0xFB, 0x9A, 0xF8, 0x83, - 0xC9, 0xE6, 0xD5, 0x57, 0xE0, 0xDB, 0xC6, 0x80, 0x17, 0x2F, 0x4A, 0x23, 0xB6, 0xBB, 0xBF, 0x7D, - 0x43, 0xD1, 0xB0, 0x34, 0xB9, 0x4B, 0x91, 0xB6, 0x61, 0xC5, 0xFD, 0xE2, 0xD5, 0x31, 0xEC, 0x51, - 0xEC, 0x76, 0x43, 0x06, 0xE4, 0x58, 0xEA, 0x2D, 0x75, 0xCD, 0xBC, 0xF4, 0xDD, 0x2A, 0x7A, 0x9F, - 0xC4, 0x0B, 0x96, 0xF0, 0xBB, 0xBA, 0x4A, 0x45, 0x2C, 0xED, 0x65, 0x1D, 0x84, 0xCD, 0x14, 0x53, - 0x76, 0xC0, 0xC3, 0x41, 0x25, 0x78, 0xDA, 0x20, 0x1B, 0x28, 0xE1, 0xCB, 0xC7, 0x4C, 0xA0, 0xCC, - 0x9F, 0x6A, 0x65, 0xEA, 0xFC, 0x29, 0xAB, 0xA2, 0x7F, 0xAA, 0x25, 0x15, 0x7E, 0x05, 0xBE, 0x83, - 0xB3, 0x3A, 0x59, 0xA8, 0x2A, 0xA1, 0xD5, 0x9A, 0x86, 0xF9, 0x09, 0x02, 0x2D, 0x27, 0x19, 0x95, - 0xBD, 0xE3, 0xD2, 0x42, 0x17, 0xF1, 0x0B, 0xA5, 0xBC, 0x50, 0x9B, 0x04, 0x34, 0x26, 0xD9, 0xD4, - 0x98, 0x44, 0x6A, 0xCC, 0x68, 0x53, 0x63, 0x92, 0x6D, 0x8D, 0x79, 0x96, 0xAE, 0x94, 0xF4, 0xA4, - 0x2F, 0x9C, 0x72, 0x1F, 0xDD, 0x2B, 0xB8, 0x66, 0xF0, 0xC2, 0xF2, 0xAB, 0x8D, 0x8E, 0xD6, 0x45, - 0xBD, 0x70, 0x51, 0x93, 0xDA, 0xA4, 0x4E, 0x36, 0x0D, 0xC5, 0x0B, 0xBA, 0x67, 0x54, 0x2E, 0x87, - 0xE6, 0xB7, 0xE9, 0xDB, 0x16, 0xBA, 0x05, 0xFD, 0x8F, 0xF3, 0xCE, 0x85, 0xF0, 0x13, 0x43, 0xAA, - 0xD9, 0xA3, 0x92, 0x8B, 0x69, 0x1E, 0xCB, 0x6C, 0x1F, 0x7D, 0x2D, 0xDB, 0xD9, 0x92, 0x83, 0x2F, - 0x31, 0x2A, 0x29, 0x18, 0x35, 0xCC, 0x38, 0xB5, 0xD1, 0x57, 0x08, 0x4B, 0x2E, 0x25, 0x25, 0x71, - 0x29, 0x0D, 0xDE, 0x2D, 0x8E, 0x7A, 0x59, 0x1A, 0x9F, 0x2E, 0x55, 0xFB, 0xF1, 0x7D, 0x96, 0x60, - 0xED, 0xF5, 0x88, 0x59, 0x0D, 0xB8, 0xEC, 0x12, 0xC3, 0x97, 0xAA, 0x3A, 0x50, 0xB1, 0x28, 0x7C, - 0xC0, 0x95, 0xE2, 0xFB, 0xA9, 0xF2, 0xA4, 0xE8, 0xA5, 0x8A, 0x6F, 0x93, 0x94, 0x8A, 0x95, 0x50, - 0x82, 0x14, 0x5C, 0x7C, 0x9A, 0x96, 0x2B, 0x81, 0xA2, 0xA0, 0xA1, 0x04, 0x1C, 0xD6, 0xC8, 0x4C, - 0x56, 0xAD, 0x55, 0x93, 0x24, 0x51, 0x6A, 0x1B, 0x85, 0xA0, 0xDA, 0xF1, 0x6F, 0xB1, 0xC2, 0x63, - 0x45, 0x1C, 0x61, 0x0C, 0x8A, 0xF3, 0x14, 0xDE, 0xF1, 0x01, 0xEC, 0x8A, 0x32, 0x74, 0xD5, 0xD7, - 0xEF, 0x9D, 0x40, 0x75, 0xFB, 0xCA, 0x7E, 0x64, 0x95, 0xA5, 0x08, 0xC8, 0xA4, 0xBA, 0x63, 0x98, - 0x18, 0x47, 0xD3, 0x20, 0x99, 0xD7, 0xD5, 0x5F, 0xC5, 0x8D, 0xE2, 0x63, 0x17, 0x8E, 0x89, 0xA7, - 0x28, 0xD3, 0x22, 0x2E, 0x02, 0x7D, 0xAC, 0x04, 0x8F, 0x34, 0x08, 0x48, 0xCA, 0xB6, 0x60, 0x66, - 0xD1, 0xE7, 0x21, 0xB0, 0x30, 0x06, 0xE4, 0x38, 0x4E, 0xEE, 0x0E, 0xC0, 0x86, 0x31, 0x55, 0xF0, - 0xA5, 0xD7, 0x3B, 0xB2, 0x5A, 0xB4, 0x48, 0xC0, 0xA5, 0xF3, 0xBA, 0xFA, 0x2A, 0x03, 0x47, 0xB5, - 0x60, 0x88, 0x7F, 0x40, 0x48, 0xA2, 0x65, 0x18, 0x82, 0xDD, 0xDE, 0x00, 0x3D, 0xC9, 0x60, 0xA0, - 0xD0, 0x83, 0xE1, 0x99, 0x43, 0xC0, 0x5A, 0xAC, 0x50, 0x1E, 0x4A, 0x91, 0x03, 0x2E, 0x13, 0x8D, - 0x22, 0xB6, 0x52, 0xFE, 0xEB, 0xED, 0x9B, 0xDF, 0x39, 0x5F, 0x9C, 0xB1, 0xEB, 0x25, 0x04, 0xB0, - 0x7A, 0x30, 0x52, 0x5B, 0x24, 0xCC, 0x2F, 0xC5, 0x6F, 0x0A, 0x46, 0xB0, 0x8D, 0xFD, 0x72, 0xB9, - 0x29, 0x5E, 0x48, 0x93, 0x08, 0x30, 0x06, 0x49, 0x6A, 0x36, 0x9B, 0x58, 0xE2, 0x81, 0x70, 0x13, - 0xC1, 0x89, 0x52, 0x76, 0x83, 0x45, 0x58, 0x33, 0xFB, 0x70, 0x76, 0x5A, 0xE7, 0x9A, 0xE8, 0x14, - 0x35, 0xBE, 0x52, 0x47, 0x39, 0xBA, 0xD3, 0xA3, 0x66, 0x1C, 0xC1, 0xC6, 0xFC, 0x3B, 0x0C, 0x09, - 0xD9, 0x04, 0xC2, 0xB3, 0x2B, 0x36, 0xCA, 0x63, 0x20, 0xED, 0xDE, 0x1D, 0x8D, 0xA2, 0x26, 0x0D, - 0xC0, 0x88, 0x1A, 0x68, 0x52, 0xB7, 0x4D, 0x13, 0xDB, 0x44, 0x08, 0xF9, 0x72, 0x47, 0x74, 0xFE, - 0x1F, 0xE7, 0xEF, 0xFE, 0x00, 0xBF, 0x9B, 0x40, 0x48, 0x8F, 0x53, 0xD3, 0x45, 0x1C, 0xA5, 0xEC, - 0x82, 0xDD, 0x72, 0x4D, 0x1B, 0xB8, 0xA6, 0x55, 0x9A, 0x8C, 0xE7, 0x06, 0x06, 0x75, 0x60, 0x77, - 0x1A, 0x87, 0xAC, 0x19, 0xC6, 0x57, 0xF5, 0xAC, 0x4B, 0xD3, 0x5F, 0x7F, 0x3C, 0xC1, 0x12, 0x20, - 0x10, 0x59, 0x5B, 0x23, 0x96, 0x0B, 0x16, 0xD5, 0xD5, 0xDF, 0x4E, 0x2E, 0x60, 0xCB, 0x3A, 0x44, - 0x56, 0xD0, 0x94, 0x02, 0xC9, 0xEB, 0x1B, 0x2C, 0x10, 0x6F, 0x05, 0x24, 0x8F, 0x0F, 0x6A, 0x45, - 0xF6, 0xA2, 0x42, 0x13, 0x96, 0x05, 0x53, 0x08, 0x13, 0x9D, 0xB5, 0x70, 0x32, 0xDA, 0xFD, 0xDE, - 0xC9, 0xD5, 0x17, 0xB1, 0x5A, 0xB3, 0xF2, 0xCE, 0x35, 0x63, 0xCB, 0x7E, 0xFD, 0x4A, 0xAE, 0x60, - 0x0E, 0x59, 0xEC, 0x66, 0xF1, 0xC6, 0x40, 0xBE, 0x3D, 0x08, 0x99, 0x3A, 0x14, 0x89, 0x0D, 0xCA, - 0xCD, 0xEB, 0x38, 0x99, 0xBF, 0xF2, 0xB8, 0x37, 0xE4, 0x4D, 0x6F, 0xB1, 0xC0, 0xCD, 0x0A, 0xED, - 0x2C, 0xC7, 0xDB, 0x85, 0xAB, 0x8C, 0xC0, 0x55, 0x46, 0x47, 0x19, 0xFE, 0xC3, 0x08, 0x9C, 0xA4, - 0x0C, 0xDD, 0xD9, 0xA7, 0xE8, 0x33, 0x58, 0xE1, 0x72, 0x4E, 0x17, 0x48, 0x2B, 0x7A, 0xAE, 0x16, - 0xC0, 0x43, 0x3D, 0x10, 0xEE, 0x53, 0x2F, 0xD6, 0x2B, 0x5E, 0xDF, 0x00, 0xD5, 0xB7, 0x01, 0x68, - 0xEB, 0x7A, 0xA5, 0xAC, 0xBC, 0x43, 0xDC, 0x35, 0xC9, 0xB9, 0xF7, 0xEF, 0xCE, 0x2F, 0x30, 0xBB, - 0x20, 0x78, 0x2A, 0x71, 0xB0, 0x32, 0xB5, 0x29, 0x2F, 0xE0, 0xC3, 0x4E, 0x6E, 0x60, 0x95, 0x37, - 0x60, 0xB3, 0x18, 0x08, 0x3D, 0x52, 0x4C, 0xBC, 0x7F, 0x51, 0xF5, 0x22, 0x34, 0xD7, 0xEE, 0x81, - 0x5D, 0xD9, 0x5E, 0x41, 0xED, 0x16, 0x4B, 0x2A, 0x6A, 0x66, 0x69, 0x21, 0xCA, 0x92, 0xE7, 0x33, - 0xBF, 0x25, 0xD3, 0xB8, 0x7F, 0x83, 0x8C, 0x6A, 0xBF, 0x8A, 0x09, 0x96, 0x08, 0x36, 0x1E, 0xB0, - 0x8C, 0x0F, 0xF1, 0x5D, 0x41, 0xC6, 0x37, 0x30, 0xA5, 0x7D, 0x8D, 0xBF, 0x00, 0xA9, 0x9B, 0x1A, - 0xE6, 0x60, 0xEB, 0xB5, 0x4E, 0xC1, 0x5E, 0x51, 0xC1, 0xB6, 0x36, 0x36, 0x1E, 0x47, 0xD4, 0x5C, - 0xD2, 0x39, 0xD2, 0xB0, 0x51, 0x75, 0x94, 0xD4, 0x98, 0xFA, 0xF3, 0xD0, 0x7B, 0x86, 0x40, 0xCA, - 0x77, 0x58, 0x87, 0x5D, 0x4B, 0xA1, 0x44, 0x72, 0x39, 0x18, 0x7F, 0xD0, 0x20, 0x54, 0x37, 0x55, - 0x35, 0x0E, 0xDA, 0x40, 0x34, 0x4B, 0xBD, 0x5F, 0x6F, 0xD0, 0x89, 0xD4, 0x9D, 0x6B, 0xEB, 0x42, - 0xE1, 0x21, 0xE0, 0xF9, 0x63, 0x39, 0x1F, 0x83, 0x90, 0x90, 0xC5, 0x2D, 0x34, 0x01, 0xD9, 0x2E, - 0x63, 0x43, 0xB0, 0xE6, 0x52, 0x4C, 0x8E, 0xF8, 0x50, 0x03, 0x63, 0x6B, 0xAA, 0x8D, 0x28, 0x2B, - 0x8A, 0x47, 0x05, 0x2C, 0xD8, 0xDD, 0xFB, 0x5F, 0xD1, 0xD5, 0xE7, 0xD6, 0x03, 0x65, 0x19, 0xD4, - 0x8F, 0xE5, 0x15, 0x74, 0xA4, 0xC0, 0x6B, 0x70, 0x0D, 0x7F, 0x63, 0xE8, 0x0F, 0x1B, 0xAA, 0xA1, - 0x36, 0x4A, 0x18, 0x60, 0xEF, 0xDB, 0x38, 0xE2, 0x33, 0xE8, 0x82, 0x18, 0x6F, 0x67, 0x3F, 0x82, - 0x83, 0x20, 0x65, 0x77, 0xE7, 0xEF, 0x31, 0x64, 0xC8, 0x7B, 0x7B, 0xDF, 0x06, 0xD1, 0x92, 0xB3, - 0xFD, 0xFD, 0xE7, 0x0C, 0xCC, 0xA8, 0x2F, 0xFA, 0x8B, 0x5D, 0xFD, 0x1E, 0xF8, 0xEC, 0xE7, 0x30, - 0x44, 0x85, 0xC9, 0xDF, 0xC4, 0x98, 0xDB, 0x6F, 0x62, 0x5E, 0xBC, 0xC8, 0xDF, 0x13, 0x35, 0x27, - 0x61, 0x8C, 0xB5, 0x98, 0x82, 0xEF, 0xF4, 0x13, 0x89, 0x51, 0xF5, 0xB1, 0xA1, 0xD6, 0x81, 0xCF, - 0x13, 0xE1, 0x8A, 0x98, 0xAF, 0x3D, 0x21, 0x3E, 0x62, 0xFB, 0x87, 0x96, 0x8E, 0xEC, 0x64, 0x12, - 0x29, 0x7F, 0x40, 0x32, 0x52, 0xF1, 0x17, 0x24, 0x07, 0x56, 0xC9, 0x5F, 0x78, 0xEF, 0x9E, 0x58, - 0x50, 0x24, 0x77, 0x2B, 0xF7, 0x19, 0x6D, 0xD4, 0xD7, 0x1E, 0x88, 0xAA, 0x8F, 0xB1, 0x55, 0xF1, - 0x26, 0x0B, 0x7F, 0xEC, 0x01, 0x39, 0xC0, 0xEB, 0x8F, 0x3F, 0x94, 0x0B, 0x5C, 0xAF, 0x3F, 0xBE, - 0xFB, 0x52, 0x3F, 0xE0, 0x1A, 0xF6, 0x39, 0x6B, 0x76, 0x48, 0x01, 0x0F, 0xED, 0x9A, 0x7E, 0xE7, - 0xF8, 0xAC, 0x6D, 0x8B, 0x99, 0x05, 0xEA, 0xA7, 0x51, 0xC0, 0x3F, 0x9C, 0x4A, 0xE1, 0x8E, 0x77, - 0xC5, 0x25, 0xA0, 0xBD, 0xAD, 0x89, 0x88, 0x60, 0x5E, 0xCA, 0x2B, 0xEA, 0x65, 0x25, 0x74, 0x50, - 0x3F, 0x41, 0xF8, 0xD9, 0x33, 0xCD, 0xCF, 0x10, 0x25, 0xED, 0x79, 0xFF, 0xB7, 0xFD, 0x26, 0xB1, - 0xEC, 0xDF, 0x55, 0xC4, 0x43, 0xF9, 0x70, 0x0A, 0x79, 0x41, 0xFC, 0x40, 0xE4, 0x01, 0x16, 0x1E, - 0x82, 0x8F, 0xB8, 0x14, 0x7C, 0x64, 0x9A, 0x29, 0x0A, 0x7E, 0x64, 0x25, 0xE3, 0x2C, 0x5E, 0x90, - 0x96, 0xDF, 0xD4, 0x23, 0x9A, 0x52, 0xD8, 0x95, 0xEC, 0xDD, 0xDF, 0x8F, 0x80, 0x32, 0xCC, 0x2A, - 0x23, 0x53, 0x1D, 0x88, 0x21, 0x85, 0xB4, 0x14, 0x8E, 0x06, 0x8B, 0x98, 0x43, 0x16, 0xA6, 0x2C, - 0xB7, 0x2A, 0x01, 0xF8, 0xD7, 0xE0, 0x28, 0x1B, 0x32, 0x0C, 0x32, 0xFF, 0x1A, 0x8E, 0xA2, 0x4F, - 0xC1, 0xE7, 0x6C, 0x95, 0x01, 0xAC, 0x02, 0x5C, 0x51, 0x6E, 0x58, 0x92, 0xC2, 0x36, 0x20, 0x47, - 0x0D, 0x3F, 0x99, 0x9F, 0x65, 0xB8, 0x08, 0xC1, 0xD3, 0x01, 0x4E, 0x66, 0xA7, 0xE8, 0x2A, 0x02, - 0x74, 0xA3, 0x36, 0xC2, 0x4F, 0xD6, 0x67, 0xC8, 0x6B, 0x1A, 0x9A, 0xAE, 0x56, 0x69, 0xBB, 0x05, - 0x1C, 0x64, 0x9D, 0xDA, 0xAC, 0xAC, 0xED, 0x65, 0x7D, 0x9B, 0x1B, 0x7B, 0x31, 0x28, 0x4E, 0x6E, - 0x1E, 0x70, 0x07, 0x10, 0xAE, 0x6D, 0x82, 0x34, 0x9F, 0x0B, 0x32, 0x0B, 0x79, 0x34, 0xB9, 0xBB, - 0x4D, 0x51, 0xDA, 0xDA, 0xDF, 0xC6, 0x2B, 0xE9, 0xF0, 0x93, 0x9D, 0x75, 0x56, 0x5F, 0x4E, 0x87, - 0x9F, 0x9C, 0xBC, 0x83, 0x2C, 0xDC, 0x39, 0x75, 0xD5, 0xB3, 0x85, 0x66, 0x71, 0xCA, 0x29, 0xDA, - 0xDF, 0xCB, 0x1D, 0x61, 0xF3, 0x4A, 0x94, 0xA4, 0x99, 0x6B, 0x90, 0x47, 0xFE, 0xB2, 0xBE, 0xB7, - 0x12, 0xAE, 0x0B, 0xFB, 0xA0, 0x0D, 0x50, 0x7A, 0xD6, 0x6B, 0x14, 0x1F, 0x85, 0x02, 0xDE, 0xB8, - 0x1A, 0xF0, 0x62, 0xB7, 0x5E, 0x15, 0x44, 0x29, 0xC5, 0xDA, 0x10, 0x22, 0xEC, 0xDC, 0x44, 0x41, - 0xEC, 0x10, 0x97, 0x03, 0x5F, 0x46, 0x61, 0x53, 0xBC, 0x15, 0xF8, 0x56, 0xF6, 0x78, 0xFF, 0x28, - 0xEB, 0x5E, 0xCF, 0x9B, 0x46, 0xBB, 0xF5, 0xF8, 0x25, 0x5A, 0x88, 0x8F, 0x6C, 0x2C, 0xC1, 0xAA, - 0x2B, 0x3C, 0x88, 0xAF, 0x36, 0xCA, 0x74, 0x6E, 0x80, 0xB4, 0x37, 0xAA, 0x3C, 0x69, 0xA8, 0xAD, - 0x15, 0x04, 0x6A, 0x9F, 0x54, 0x2F, 0xF1, 0x97, 0x01, 0x88, 0xE3, 0x67, 0x6D, 0xF0, 0x2C, 0x40, - 0x15, 0x10, 0x5A, 0x73, 0x1C, 0x44, 0x90, 0x02, 0x5F, 0xD0, 0x79, 0x09, 0x2F, 0x49, 0xBC, 0xBB, - 0xF1, 0x72, 0x3A, 0x65, 0x90, 0x89, 0x15, 0x3B, 0x8B, 0x23, 0xA4, 0xD4, 0xA8, 0x1C, 0x23, 0x56, - 0x0C, 0xCF, 0xC7, 0x73, 0x55, 0xDB, 0x75, 0x06, 0xC1, 0x5C, 0x57, 0x80, 0x10, 0x81, 0x2A, 0x50, - 0x76, 0x9E, 0x5B, 0xA8, 0x80, 0xFE, 0x1F, 0x82, 0x2D, 0x1D, 0xEC, 0xB7, 0x6F, 0x29, 0xE3, 0x18, - 0x48, 0xC4, 0x4B, 0x5E, 0x2F, 0x71, 0x46, 0x77, 0x98, 0xA3, 0x55, 0xD7, 0xA2, 0x33, 0x0E, 0x07, - 0x31, 0xC6, 0x24, 0xB6, 0x32, 0x25, 0x3B, 0x25, 0xB1, 0x11, 0x0A, 0xFF, 0x00, 0x41, 0x80, 0x0F, - 0xA9, 0x82, 0x12, 0x44, 0xB0, 0x64, 0x34, 0x61, 0x90, 0x28, 0xFF, 0x8C, 0x74, 0xFA, 0x85, 0xE8, - 0xA4, 0x15, 0x81, 0x31, 0x8E, 0x2A, 0x1B, 0x2B, 0xFB, 0x68, 0x94, 0x9D, 0x54, 0x40, 0x0B, 0x22, - 0x63, 0xFC, 0xD3, 0x57, 0xA0, 0x1B, 0x1C, 0x74, 0x03, 0xDA, 0xB2, 0xD3, 0x1C, 0x1C, 0xED, 0x50, - 0xD5, 0x96, 0xBF, 0x52, 0x30, 0x0E, 0xA1, 0x6E, 0x0D, 0xCD, 0x13, 0xFD, 0xAB, 0x1D, 0xA5, 0xB9, - 0xB2, 0xEF, 0x07, 0x9A, 0xFB, 0xE2, 0x45, 0xEE, 0x71, 0x4F, 0xB9, 0x92, 0x32, 0x36, 0x4F, 0x95, - 0xBB, 0x78, 0xA9, 0xE0, 0x09, 0x34, 0x19, 0x49, 0x28, 0x53, 0xC8, 0xCA, 0x15, 0x2F, 0x8A, 0xC1, - 0xC2, 0x80, 0x65, 0x8D, 0x85, 0x28, 0xEA, 0x38, 0x2C, 0xA1, 0x71, 0x51, 0xBC, 0x52, 0xCA, 0x81, - 0x07, 0x90, 0x5D, 0x3D, 0x39, 0x3B, 0x7B, 0x77, 0x56, 0xA0, 0xBB, 0x7D, 0xA2, 0x84, 0x83, 0x9D, - 0xD8, 0x3C, 0x53, 0xB2, 0xB5, 0x19, 0x1C, 0xD4, 0x50, 0x15, 0xEC, 0x1C, 0xE0, 0x79, 0x0A, 0xEB, - 0xB3, 0xA6, 0x57, 0xC2, 0xD1, 0x8D, 0x68, 0xD4, 0x1B, 0xE3, 0xCB, 0x02, 0x4C, 0x4E, 0x4B, 0x01, - 0x69, 0x65, 0xC2, 0x3D, 0xA6, 0x93, 0x95, 0x55, 0xC1, 0x1C, 0x87, 0x0C, 0x66, 0xC9, 0xB3, 0x6A, - 0xCA, 0x94, 0x02, 0x8F, 0x3A, 0x38, 0xD8, 0xCA, 0xB0, 0x06, 0x58, 0x59, 0xA5, 0xDC, 0x28, 0x37, - 0xA2, 0x6D, 0x1D, 0x8C, 0xD1, 0x06, 0xBB, 0x00, 0x42, 0xC8, 0xA2, 0x83, 0xD1, 0x29, 0x52, 0x8E, - 0xFF, 0x07, 0x59, 0xC3, 0x5E, 0x9B, 0xAA, 0x0D, 0x32, 0x31, 0x00, 0x67, 0x8D, 0x48, 0x95, 0x0D, - 0x5F, 0xF9, 0x24, 0xE0, 0xBD, 0x70, 0xEE, 0xD5, 0xC2, 0xD1, 0xC6, 0x11, 0x47, 0xE5, 0xA5, 0xAA, - 0x3D, 0x5C, 0x1C, 0x58, 0x3D, 0xBF, 0x34, 0x80, 0x07, 0x15, 0x9F, 0x47, 0x96, 0xD2, 0xA2, 0xCF, - 0x98, 0x8E, 0xE7, 0x10, 0x0F, 0xB9, 0xD8, 0x87, 0x66, 0x96, 0x22, 0x8E, 0x67, 0x46, 0xAA, 0x0F, - 0xC4, 0xE7, 0xE2, 0x5C, 0xE5, 0xC1, 0xC2, 0x47, 0x16, 0x68, 0x55, 0x8B, 0x1F, 0xFA, 0x83, 0x75, - 0x0D, 0x15, 0xBC, 0xC9, 0xD3, 0xEA, 0x19, 0xA2, 0x9C, 0x51, 0xCC, 0xD3, 0xD6, 0xE5, 0x03, 0x66, - 0xFA, 0x93, 0x6B, 0x1A, 0x4B, 0x12, 0x2E, 0xD8, 0xDF, 0x8E, 0xB2, 0xC6, 0x3F, 0xA7, 0x9E, 0x41, - 0xB4, 0x7E, 0xB0, 0xA2, 0xB1, 0xC5, 0xFB, 0xC7, 0xD5, 0x32, 0x1E, 0x2A, 0x5F, 0x64, 0xB1, 0xF9, - 0xCE, 0x0A, 0xC6, 0xC3, 0xDA, 0xF3, 0x58, 0x13, 0xB4, 0x85, 0xFD, 0x81, 0xD3, 0xB6, 0x07, 0xC0, - 0x64, 0xE7, 0x6E, 0x9F, 0xA7, 0x3E, 0xCF, 0xD7, 0xF8, 0xCA, 0xCC, 0xEC, 0xAC, 0xF9, 0xE2, 0xF6, - 0x91, 0x36, 0xA2, 0x28, 0xB7, 0xFC, 0x83, 0x2C, 0xCA, 0xF6, 0xF4, 0x12, 0x8E, 0x43, 0x61, 0x42, - 0x1F, 0x57, 0xD7, 0x41, 0xF3, 0xA9, 0x5A, 0xF4, 0x22, 0x54, 0x88, 0xC0, 0x8B, 0x17, 0xAA, 0x5B, - 0x7D, 0x2C, 0xF7, 0x7E, 0xFB, 0x56, 0x75, 0xBB, 0xAA, 0x0D, 0xBE, 0x3D, 0xEB, 0xD4, 0xAA, 0xFE, - 0x6E, 0x82, 0xA1, 0x8E, 0xF0, 0x78, 0x94, 0xAC, 0x29, 0xB8, 0x94, 0x53, 0x1E, 0x2F, 0x14, 0x88, - 0x8C, 0xC8, 0x83, 0xFA, 0x82, 0x67, 0x2F, 0x5C, 0x53, 0xE7, 0x23, 0x08, 0xE0, 0xE8, 0xF7, 0xAA, - 0x40, 0xE4, 0x7A, 0x49, 0xAE, 0xA3, 0xC6, 0xE8, 0x40, 0xFA, 0x54, 0xD5, 0xBA, 0xE8, 0x31, 0x02, - 0x57, 0xC8, 0xAE, 0x6B, 0x19, 0x91, 0xEE, 0x9A, 0x47, 0x11, 0x04, 0x30, 0x13, 0x10, 0xDD, 0x24, - 0x5F, 0x9F, 0x63, 0x60, 0xB9, 0xE1, 0x00, 0x21, 0x26, 0xB4, 0x30, 0x98, 0xA4, 0x2D, 0x57, 0x6B, - 0x66, 0x3B, 0x9A, 0xF6, 0x95, 0xD1, 0x0A, 0x37, 0x8A, 0x59, 0xC9, 0x7E, 0xB5, 0x2C, 0x4E, 0x98, - 0x3F, 0x58, 0x64, 0xA0, 0x13, 0xCA, 0xCF, 0x82, 0x44, 0xEE, 0x63, 0xF8, 0x80, 0x67, 0x0E, 0x0B, - 0xFB, 0x90, 0xA7, 0x65, 0x07, 0x46, 0x2F, 0xB6, 0x46, 0xE3, 0xD9, 0x5F, 0x42, 0xE2, 0xE5, 0x87, - 0xF3, 0x93, 0xB3, 0x72, 0x65, 0x03, 0x6D, 0x2E, 0xA0, 0x10, 0x71, 0xB0, 0xC5, 0x0D, 0xF5, 0xC5, - 0xFB, 0x9F, 0xCF, 0xCF, 0x3F, 0xBE, 0x3B, 0x7B, 0xB5, 0x7B, 0x08, 0xC7, 0x21, 0xE7, 0x1F, 0x7E, - 0x79, 0x7B, 0x7A, 0x31, 0xBA, 0xC3, 0x6A, 0x76, 0xB0, 0xC3, 0x49, 0x0C, 0x83, 0x87, 0xDF, 0xAC, - 0x04, 0x5B, 0x6F, 0x56, 0x7E, 0x80, 0x36, 0x99, 0x2B, 0x52, 0xE6, 0x18, 0x54, 0x33, 0xC7, 0x3C, - 0x35, 0xAC, 0xC4, 0xAE, 0x41, 0x9E, 0x3E, 0x0E, 0xB2, 0x32, 0x0F, 0xB0, 0x3D, 0x28, 0xA7, 0x8E, - 0x11, 0xB9, 0xA6, 0x20, 0x4B, 0x1D, 0x57, 0x41, 0xE4, 0xC7, 0xAB, 0x1D, 0xB6, 0x3B, 0x9B, 0xBF, - 0x1E, 0x1E, 0xB5, 0xE4, 0x31, 0xF2, 0xA3, 0x96, 0xFC, 0xF5, 0x0A, 0xFD, 0xA3, 0xD1, 0xFF, 0x0B, - 0xB4, 0xEA, 0x2E, 0x52, 0x3B, 0x5A, 0x00, 0x00 +const char PAGE_NOFILES[] = { + 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xED, 0x3C, 0x89, 0x72, 0xDB, 0xC6, + 0x92, 0xBF, 0x82, 0x20, 0xF5, 0x4C, 0x62, 0x09, 0x90, 0xB8, 0x78, 0x8B, 0xF2, 0x26, 0xB1, 0xFD, + 0xA2, 0x2D, 0x2B, 0x76, 0x49, 0xF2, 0x7A, 0x5F, 0x39, 0x2E, 0x15, 0x48, 0x0C, 0x45, 0xAC, 0x41, + 0x80, 0x02, 0x86, 0xA2, 0x64, 0x99, 0xFB, 0xED, 0xDB, 0xDD, 0x33, 0xB8, 0x78, 0xE9, 0x88, 0xDF, + 0x26, 0x5B, 0xF5, 0xA2, 0x90, 0x00, 0xE6, 0xE8, 0xE9, 0xE9, 0xBB, 0x1B, 0x43, 0x1F, 0xCD, 0xF8, + 0x3C, 0x3C, 0x3E, 0x9A, 0x31, 0xCF, 0x3F, 0x3E, 0x4A, 0xF9, 0x5D, 0xC8, 0x8E, 0xB1, 0xE5, 0x7E, + 0x1A, 0x47, 0xDC, 0x98, 0x7A, 0xF3, 0x20, 0xBC, 0x1B, 0xA4, 0x5E, 0x94, 0x1A, 0x29, 0x4B, 0x82, + 0xE9, 0xD0, 0x98, 0xA7, 0x06, 0x67, 0xB7, 0xDC, 0x48, 0x83, 0xAF, 0xCC, 0xF0, 0xFC, 0xFF, 0x5E, + 0xA6, 0x7C, 0x60, 0x99, 0xE6, 0xDF, 0x86, 0xC6, 0x8A, 0x8D, 0xBF, 0x04, 0x7C, 0x4F, 0x2F, 0x81, + 0xC3, 0x56, 0x78, 0x5C, 0xDC, 0xAE, 0xC7, 0xB1, 0x7F, 0x57, 0x59, 0x42, 0xFD, 0x95, 0x85, 0x37, + 0x8C, 0x07, 0x13, 0x4F, 0xF9, 0x8D, 0x2D, 0x99, 0xAA, 0xE7, 0xCF, 0xFA, 0x4F, 0x49, 0xE0, 0x85, + 0x7A, 0x09, 0x87, 0x12, 0x2C, 0x77, 0x71, 0x3B, 0x0C, 0x83, 0x88, 0x19, 0x33, 0x16, 0x5C, 0xCD, + 0x60, 0xAD, 0xA6, 0x6B, 0xF7, 0xDA, 0x5D, 0xCB, 0x75, 0x86, 0x93, 0x38, 0x8C, 0x93, 0xC1, 0x8F, + 0x8E, 0xE3, 0x0C, 0xC7, 0xDE, 0xE4, 0xCB, 0x55, 0x12, 0x2F, 0x23, 0xDF, 0x90, 0xAD, 0xD3, 0xE9, + 0x74, 0xCD, 0xBD, 0x71, 0xC8, 0xEE, 0xC7, 0x71, 0xE2, 0xB3, 0x64, 0x60, 0x0E, 0xC5, 0x8D, 0x91, + 0x2E, 0xBC, 0x49, 0x10, 0x5D, 0x41, 0xC3, 0xDC, 0xBB, 0x35, 0x56, 0x81, 0xCF, 0x67, 0xB4, 0x83, + 0x35, 0xF7, 0xEF, 0x57, 0xB3, 0x80, 0x33, 0x1A, 0xC1, 0x06, 0x51, 0xBC, 0x4A, 0xBC, 0xC5, 0x70, + 0xE1, 0xF9, 0x3E, 0x0E, 0xB7, 0xE7, 0xF3, 0x35, 0x9F, 0xDD, 0xD3, 0xE6, 0xBD, 0x30, 0xB8, 0x8A, + 0x06, 0x21, 0x9B, 0xF2, 0x75, 0x93, 0x16, 0x39, 0xE6, 0xB8, 0xDF, 0x63, 0x9E, 0x1C, 0x73, 0x5F, + 0xDF, 0x6A, 0x9A, 0xE5, 0x4D, 0xC4, 0x84, 0xEA, 0xA8, 0xBC, 0x69, 0x76, 0x9F, 0x2D, 0xD5, 0xDB, + 0xBF, 0xE7, 0x1B, 0x96, 0x20, 0xC9, 0x42, 0x89, 0x02, 0x8F, 0x17, 0xD9, 0xB6, 0xE0, 0x76, 0x60, + 0x2D, 0x6E, 0x95, 0x34, 0x0E, 0x03, 0x5F, 0xF9, 0xD1, 0xF7, 0x7D, 0x89, 0x9B, 0x91, 0xF2, 0x24, + 0x58, 0x30, 0x3F, 0x47, 0x68, 0x10, 0xF1, 0x99, 0x11, 0x4F, 0x0D, 0x7E, 0xB7, 0x60, 0xF5, 0xD8, + 0xF7, 0xB5, 0xFB, 0x1D, 0xE4, 0xEB, 0xE3, 0xDF, 0xDA, 0xBB, 0x5F, 0xC4, 0x69, 0xC0, 0x83, 0x38, + 0x1A, 0x24, 0x2C, 0xF4, 0x78, 0x70, 0xC3, 0x86, 0x7E, 0x90, 0x2E, 0x42, 0xEF, 0x6E, 0x30, 0x0E, + 0xE3, 0xC9, 0x97, 0x9C, 0x3C, 0xC8, 0x74, 0xC5, 0x6A, 0x03, 0xE6, 0x44, 0x21, 0x9F, 0x4D, 0xE2, + 0xC4, 0xA3, 0x89, 0x51, 0x1C, 0xB1, 0x8C, 0x57, 0x93, 0xC9, 0x64, 0xDD, 0xF4, 0x26, 0x08, 0xE7, + 0xBE, 0x60, 0xD4, 0x0E, 0xF6, 0x99, 0xA6, 0x99, 0x0D, 0x54, 0x3C, 0xDD, 0x1B, 0x4C, 0xE3, 0xC9, + 0x32, 0x85, 0xEB, 0x2C, 0x06, 0x0A, 0x94, 0xA6, 0xAE, 0x9B, 0x0B, 0x2F, 0x62, 0xE1, 0xFD, 0xDC, + 0x4B, 0xAE, 0x82, 0xC8, 0x18, 0xC7, 0x9C, 0xC7, 0xF3, 0x81, 0x0D, 0xC8, 0xEC, 0x96, 0x09, 0x49, + 0xAD, 0x0D, 0x4A, 0x65, 0x34, 0x4C, 0x3C, 0x3F, 0x58, 0xA6, 0x03, 0x94, 0xB9, 0x4C, 0xD8, 0xC7, + 0xF1, 0xAD, 0x91, 0xCE, 0x3C, 0x3F, 0x5E, 0x0D, 0x4C, 0x05, 0x67, 0xE1, 0x27, 0xB9, 0x1A, 0x7B, + 0x75, 0x53, 0xC7, 0xBF, 0xA6, 0xD9, 0xD6, 0x86, 0x8F, 0x19, 0x24, 0x31, 0x35, 0x48, 0x31, 0x72, + 0xAA, 0x01, 0xC1, 0xB2, 0x0E, 0x14, 0x04, 0x68, 0xBB, 0xDF, 0xA6, 0xE8, 0x61, 0x41, 0x6F, 0xE3, + 0x5F, 0xB6, 0x03, 0xD9, 0x58, 0xDA, 0x13, 0xC8, 0x85, 0x91, 0xA0, 0x18, 0x65, 0xBB, 0x73, 0x90, + 0x36, 0x45, 0x1F, 0x4A, 0xF1, 0x8E, 0x2E, 0x49, 0xC9, 0x4D, 0x89, 0x9A, 0xC6, 0xC9, 0x1C, 0x16, + 0x89, 0x78, 0x12, 0x87, 0xF7, 0x55, 0x49, 0x10, 0x9A, 0xE4, 0x2D, 0x79, 0x3C, 0x94, 0x72, 0xEB, + 0x20, 0x21, 0xB3, 0xED, 0x74, 0x70, 0x37, 0x36, 0x34, 0x3C, 0x49, 0xB9, 0xDB, 0xED, 0xF6, 0x3E, + 0x46, 0x16, 0xAD, 0xC1, 0xDC, 0xBB, 0x62, 0x42, 0xCE, 0xB6, 0xD9, 0x0B, 0x22, 0xF7, 0x38, 0xF6, + 0x06, 0x51, 0xCA, 0xB8, 0xB2, 0x87, 0x7F, 0xDD, 0x2A, 0x97, 0x1F, 0x1C, 0x6B, 0xC4, 0x06, 0x4F, + 0xC0, 0xA0, 0x09, 0xDD, 0x29, 0x33, 0x47, 0x61, 0x5E, 0xCA, 0x0C, 0x90, 0xD5, 0x78, 0xC9, 0x95, + 0xA6, 0xD5, 0x4E, 0xF5, 0x02, 0xEE, 0x56, 0x5F, 0x95, 0xE0, 0x42, 0x0B, 0xEE, 0xAB, 0xAC, 0xEE, + 0x74, 0xBC, 0x29, 0xEB, 0x0F, 0x61, 0x06, 0x52, 0x12, 0xAC, 0xDA, 0x33, 0xB6, 0xA6, 0x9B, 0xD0, + 0xD9, 0xCB, 0x3A, 0x2C, 0xD3, 0xD6, 0xAD, 0x6E, 0x5B, 0xB7, 0x1D, 0x47, 0x6F, 0x76, 0x34, 0x89, + 0x03, 0xD2, 0x7A, 0xB1, 0xA1, 0x67, 0x42, 0x7C, 0xC7, 0x3C, 0xCA, 0x45, 0x21, 0x88, 0x88, 0x9F, + 0x42, 0x22, 0xAA, 0x83, 0x4D, 0xC1, 0xF9, 0x95, 0x60, 0xB5, 0x6B, 0x9A, 0xC3, 0x92, 0x2D, 0x9D, + 0xB0, 0x88, 0xB3, 0x64, 0xD3, 0xBC, 0xCD, 0x03, 0xDF, 0x0F, 0x99, 0x70, 0x49, 0xF1, 0x72, 0x32, + 0x33, 0xD0, 0x22, 0x00, 0x3D, 0xE7, 0x5E, 0x14, 0x2C, 0x96, 0x21, 0xD9, 0x97, 0xE1, 0xFE, 0x9E, + 0xC9, 0x32, 0x49, 0x81, 0x44, 0x8B, 0x38, 0x20, 0xE0, 0x8F, 0x94, 0x18, 0xE2, 0xDB, 0xC2, 0x4B, + 0x00, 0xA3, 0xE1, 0x01, 0x7F, 0xF0, 0x44, 0x79, 0xDE, 0x21, 0x82, 0xF3, 0xF8, 0xAB, 0xB1, 0x4C, + 0xD1, 0x23, 0xB1, 0x90, 0x4D, 0xB8, 0x40, 0x07, 0xF7, 0xBA, 0xD5, 0xB8, 0xD9, 0x40, 0x34, 0x37, + 0x16, 0x09, 0x6C, 0x23, 0xB9, 0x3B, 0x6C, 0x48, 0x1D, 0xA7, 0xEB, 0x8D, 0xBB, 0x1B, 0xE6, 0xC1, + 0x66, 0x1D, 0xDF, 0x73, 0x2B, 0x50, 0xA4, 0xB1, 0xD5, 0x2B, 0x6D, 0xC2, 0xEA, 0x56, 0x9A, 0xC8, + 0x00, 0x57, 0x9A, 0x06, 0x3B, 0x66, 0x0E, 0xB6, 0x67, 0x6E, 0x99, 0xEE, 0x1D, 0xC8, 0xDA, 0xBD, + 0x8E, 0xD9, 0x37, 0x37, 0x90, 0xB5, 0x6C, 0x7B, 0xEC, 0x9A, 0x84, 0x6C, 0x30, 0xBF, 0xBA, 0x97, + 0x4C, 0x9D, 0x79, 0xD1, 0xA6, 0xD9, 0xEE, 0xE4, 0xD6, 0xAB, 0xAC, 0xFF, 0xE4, 0x24, 0xC4, 0x5C, + 0x89, 0xC2, 0x0E, 0x7B, 0x62, 0xE2, 0xDF, 0xC6, 0xBA, 0x9D, 0x09, 0xFE, 0x3D, 0x5B, 0x9D, 0x50, + 0x3E, 0xAE, 0x12, 0x76, 0xF7, 0x14, 0xB3, 0x51, 0x99, 0x48, 0x58, 0x13, 0x9A, 0x87, 0xB7, 0xED, + 0x98, 0x52, 0x09, 0xB3, 0xB1, 0x0F, 0x6D, 0xF3, 0xCF, 0xDC, 0x51, 0x08, 0x48, 0x81, 0x86, 0x7C, + 0xD1, 0x8B, 0xDB, 0x41, 0x35, 0x1E, 0x20, 0xCF, 0x5F, 0x74, 0x56, 0xA4, 0x06, 0xFB, 0x82, 0x68, + 0xB1, 0xE4, 0x9F, 0x30, 0x76, 0x19, 0x4D, 0x83, 0x90, 0x7D, 0x1E, 0x0C, 0xB2, 0xFD, 0xE0, 0xA3, + 0xB1, 0x5C, 0x84, 0xB1, 0xE7, 0x1B, 0xE3, 0x25, 0xD8, 0x9C, 0x7F, 0x99, 0xA5, 0xFF, 0x5B, 0xB3, + 0x34, 0x3C, 0xA8, 0xDC, 0xED, 0xF1, 0xC4, 0xF4, 0xD9, 0x86, 0x92, 0xB9, 0x9D, 0x71, 0xCF, 0xF7, + 0x9E, 0xC4, 0x54, 0xE9, 0x05, 0xFF, 0xC5, 0xDA, 0xBF, 0x0E, 0x6B, 0x1D, 0x6B, 0x6C, 0xFA, 0x9B, + 0x31, 0xA8, 0x35, 0xEE, 0xF8, 0xBD, 0xF6, 0xD3, 0x58, 0x2B, 0xB4, 0xFD, 0x5F, 0xAC, 0xFD, 0x8B, + 0xB3, 0xD6, 0xEE, 0xF4, 0xBD, 0xF1, 0x24, 0x4B, 0x5C, 0xA6, 0x71, 0x0C, 0x14, 0x39, 0x90, 0xB7, + 0x58, 0x5D, 0xB3, 0xB7, 0x0B, 0xF6, 0x23, 0x52, 0x97, 0xAD, 0x04, 0xE4, 0x4F, 0x58, 0x72, 0x1E, + 0xFB, 0x5E, 0x91, 0xEC, 0x10, 0xC9, 0xF2, 0xAC, 0x78, 0x1A, 0xDC, 0x32, 0x7F, 0xF8, 0x15, 0x62, + 0x76, 0x9F, 0xDD, 0x62, 0x19, 0x01, 0x24, 0x51, 0x62, 0x25, 0x60, 0x99, 0x98, 0x8A, 0x62, 0x8E, + 0x05, 0x22, 0x8B, 0x0D, 0xE6, 0xB0, 0xA8, 0x38, 0x64, 0x79, 0x12, 0xDD, 0xA3, 0xE4, 0x4F, 0x43, + 0x70, 0xA9, 0x94, 0x41, 0xED, 0xCC, 0x88, 0xB7, 0x5B, 0xCB, 0xEE, 0xD6, 0xD5, 0x24, 0xAA, 0x94, + 0x2E, 0x80, 0xC0, 0xDD, 0xEF, 0xC9, 0xF2, 0x2C, 0xB3, 0x9A, 0x01, 0x56, 0xB2, 0xC3, 0x72, 0xA7, + 0xD0, 0xB5, 0xBD, 0x73, 0x65, 0xF7, 0xBE, 0xE9, 0x03, 0xBB, 0xA0, 0x63, 0x1E, 0x85, 0x96, 0xF2, + 0x64, 0xCC, 0x37, 0x2C, 0x14, 0x7C, 0xB3, 0x12, 0x35, 0xD8, 0xDA, 0x70, 0xBB, 0xE6, 0x20, 0x94, + 0x5F, 0x90, 0x26, 0x63, 0xFA, 0x0E, 0x72, 0xFC, 0x38, 0x65, 0xF8, 0x97, 0xD1, 0x01, 0x33, 0xEA, + 0x92, 0x94, 0xD8, 0x72, 0xC1, 0x4C, 0x48, 0x28, 0x1A, 0xDA, 0x29, 0x24, 0x36, 0xFE, 0xED, 0x4B, + 0x92, 0x9F, 0x48, 0xBE, 0x4A, 0x2E, 0x3A, 0xC5, 0xBF, 0x0C, 0xBD, 0x6A, 0x25, 0xC0, 0x94, 0xD8, + 0x65, 0xBD, 0x9B, 0x22, 0xDE, 0xC9, 0xB0, 0x97, 0x42, 0xE3, 0x36, 0xDB, 0x6C, 0xFE, 0xF4, 0xAD, + 0x6C, 0xA3, 0xF3, 0x07, 0xB9, 0xBD, 0x6E, 0xCE, 0x02, 0x9F, 0x5D, 0x06, 0xBC, 0xA2, 0x21, 0xEB, + 0x7F, 0x9F, 0x33, 0x3F, 0xF0, 0x94, 0xFA, 0x1C, 0x6C, 0xB6, 0x90, 0xF8, 0x6E, 0x07, 0x38, 0xAE, + 0xDD, 0x6F, 0xC8, 0xA8, 0xE8, 0x6B, 0xF7, 0x10, 0x52, 0x36, 0x29, 0x9D, 0x24, 0x8C, 0x45, 0x0A, + 0x84, 0xBA, 0x30, 0x3F, 0xAF, 0xD1, 0x75, 0x3B, 0xDD, 0xBD, 0xF3, 0xA9, 0x7E, 0xB7, 0x3E, 0x6A, + 0x89, 0xF2, 0xE6, 0x11, 0x0F, 0x38, 0x5C, 0x5E, 0x9F, 0xBF, 0x77, 0x5E, 0x29, 0x3C, 0x8E, 0x43, + 0x65, 0x01, 0x16, 0xFA, 0xA8, 0x25, 0x9A, 0x8F, 0x5A, 0xA2, 0x14, 0x4A, 0xD5, 0xB0, 0x23, 0x3F, + 0xB8, 0x51, 0x26, 0xA1, 0x97, 0xA6, 0x23, 0x95, 0x4C, 0x8B, 0x0A, 0xB3, 0xB1, 0x6A, 0xA6, 0x10, + 0xE0, 0x91, 0x8A, 0x90, 0xB1, 0x2D, 0x81, 0x0F, 0x4C, 0xF2, 0xB2, 0xC1, 0x22, 0xA3, 0x50, 0x95, + 0x59, 0xC2, 0xA6, 0x23, 0x75, 0xC6, 0xF9, 0x22, 0x1D, 0xB4, 0x5A, 0x57, 0x01, 0x9F, 0x2D, 0xC7, + 0xCD, 0x49, 0x3C, 0x6F, 0x8D, 0xFD, 0x04, 0xF8, 0xD6, 0x7A, 0x13, 0x2E, 0x03, 0xFF, 0xB7, 0x5F, + 0x54, 0x85, 0x83, 0x08, 0x33, 0x3E, 0x52, 0x2F, 0x21, 0xB6, 0x8D, 0xBE, 0x00, 0xC8, 0xF4, 0xE6, + 0x2A, 0x5F, 0x84, 0xCD, 0x01, 0x12, 0xF1, 0x55, 0x3E, 0xDC, 0x04, 0x6C, 0xF5, 0x73, 0x7C, 0x3B, + 0x52, 0x31, 0x7E, 0xB6, 0x1C, 0x13, 0xBE, 0x6C, 0xD3, 0x84, 0x59, 0x57, 0xC2, 0xA5, 0x60, 0x4A, + 0x3E, 0x52, 0xE9, 0x16, 0x54, 0x84, 0xD5, 0xDB, 0xA6, 0x8E, 0x03, 0x34, 0xA0, 0x9D, 0x17, 0xB2, + 0xBA, 0xA5, 0x2B, 0x86, 0xA5, 0xC1, 0xF0, 0x85, 0xC7, 0x67, 0x8A, 0x3F, 0x52, 0x4F, 0x3B, 0x08, + 0xC2, 0xEA, 0xBA, 0xD7, 0x8E, 0x03, 0x10, 0xBB, 0xAE, 0x62, 0xB4, 0x43, 0xA7, 0x07, 0xA3, 0xDA, + 0x76, 0xD8, 0x86, 0xCB, 0xB5, 0xDB, 0x87, 0x6F, 0x57, 0xE9, 0x43, 0x8F, 0xD3, 0xC7, 0x26, 0x3B, + 0xB4, 0x1C, 0x57, 0xE9, 0x99, 0xD7, 0x1D, 0x4B, 0x31, 0xDC, 0x9E, 0x62, 0x99, 0xD0, 0x65, 0x99, + 0xED, 0xD0, 0xE8, 0x99, 0x70, 0xE3, 0xB8, 0xA1, 0x03, 0x40, 0xAE, 0x6D, 0x18, 0xEA, 0xBA, 0x8A, + 0x03, 0xD3, 0xFB, 0x4E, 0x08, 0x43, 0x3B, 0x21, 0xC0, 0x04, 0x20, 0xBD, 0x6B, 0xEC, 0x71, 0x14, + 0xF8, 0xEE, 0x3A, 0xD7, 0x30, 0xC5, 0xC1, 0x45, 0xE1, 0xC1, 0x0D, 0x0D, 0x39, 0x02, 0x6E, 0x60, + 0xFC, 0x35, 0x3C, 0xC2, 0xC8, 0x3E, 0x2E, 0x4C, 0x40, 0x0C, 0x04, 0x1C, 0xCA, 0x55, 0xAE, 0x71, + 0x6D, 0x03, 0x71, 0x28, 0x10, 0x20, 0xC4, 0xAC, 0x10, 0xA1, 0x39, 0xD7, 0xB8, 0xBA, 0x81, 0x58, + 0x48, 0xD4, 0x0D, 0xC2, 0xDD, 0x10, 0x9B, 0xB3, 0x94, 0x6B, 0xC4, 0x41, 0xAC, 0x8B, 0xE8, 0x1A, + 0xB4, 0x7F, 0x7C, 0x68, 0xD3, 0x18, 0x18, 0x82, 0x33, 0xEC, 0x6B, 0x44, 0x00, 0xF6, 0x8F, 0x50, + 0x04, 0x10, 0x47, 0xAC, 0x63, 0xF4, 0xAC, 0x6B, 0xA3, 0x63, 0x2A, 0x88, 0x05, 0x62, 0x80, 0x08, + 0xF4, 0x90, 0x27, 0x2E, 0xE2, 0x09, 0x00, 0x61, 0x69, 0x17, 0x11, 0xE9, 0x29, 0x88, 0xBA, 0xAD, + 0x74, 0x42, 0x5A, 0x17, 0xF6, 0x6F, 0x74, 0x14, 0x17, 0xF6, 0xD9, 0x01, 0x72, 0xC3, 0xFE, 0x61, + 0x61, 0xB8, 0x03, 0x12, 0x51, 0x67, 0x08, 0x03, 0xAF, 0x2D, 0x07, 0xC1, 0x8A, 0x99, 0x8E, 0x22, + 0x28, 0x8B, 0x5B, 0x76, 0xBB, 0x0A, 0x6C, 0x18, 0x56, 0xA2, 0xD5, 0x2C, 0x98, 0x09, 0x3D, 0x21, + 0x62, 0x09, 0x2B, 0xC1, 0x7A, 0x02, 0x47, 0xE8, 0x0D, 0x69, 0x07, 0xD0, 0x8C, 0x64, 0xC6, 0x3D, + 0x7D, 0x25, 0x46, 0xF7, 0x80, 0xA0, 0xD7, 0x46, 0xAF, 0x8F, 0x3B, 0x25, 0x52, 0x77, 0x1C, 0x0E, + 0x1F, 0x22, 0x48, 0xB3, 0xCD, 0x8B, 0xBB, 0xAC, 0x13, 0xAF, 0x70, 0x81, 0x0E, 0xD1, 0x6E, 0x14, + 0x77, 0xA2, 0xEB, 0x2B, 0xC8, 0x52, 0x0B, 0x85, 0x09, 0x2E, 0x57, 0xF0, 0x01, 0xE1, 0x3D, 0x56, + 0x8E, 0x20, 0x96, 0x89, 0x72, 0x85, 0xC8, 0x52, 0x36, 0xF5, 0xF8, 0x4D, 0x90, 0xCC, 0x57, 0x10, + 0xF3, 0xC0, 0x30, 0x18, 0x00, 0xA3, 0x3D, 0xF8, 0xA0, 0xF6, 0x3C, 0x42, 0x83, 0x56, 0xAB, 0x55, + 0xB3, 0xA4, 0x45, 0xA7, 0x01, 0x9F, 0xCC, 0x7E, 0x06, 0x73, 0x13, 0xB2, 0xBB, 0x16, 0x29, 0xB4, + 0xF1, 0xF1, 0xF5, 0xCF, 0x1F, 0x4E, 0x5A, 0x1C, 0x4C, 0x44, 0xEB, 0x02, 0x35, 0x95, 0x9F, 0xF3, + 0xE5, 0x74, 0xFA, 0xD7, 0x50, 0x30, 0xB3, 0xEF, 0x5E, 0xF7, 0x6C, 0x84, 0xD8, 0x31, 0x9B, 0x28, + 0x87, 0x36, 0x12, 0xD9, 0x05, 0x36, 0xB4, 0xFB, 0xDC, 0xB2, 0x3A, 0xD8, 0xD6, 0xC3, 0xB6, 0xBE, + 0x8B, 0xB7, 0x7D, 0xE0, 0x45, 0x8F, 0x2E, 0xAE, 0x9D, 0x77, 0xA1, 0x10, 0xB6, 0xBB, 0x44, 0xFA, + 0xFC, 0x0E, 0x45, 0x98, 0x3A, 0x8D, 0x4E, 0x4F, 0x4E, 0x34, 0x72, 0x10, 0x46, 0x19, 0xB0, 0x91, + 0xAD, 0x06, 0x8C, 0xEB, 0xE7, 0x28, 0xC8, 0x07, 0x3B, 0x1F, 0x41, 0x03, 0x68, 0x9A, 0x98, 0x45, + 0xC0, 0xFA, 0x19, 0xFC, 0xBE, 0x58, 0x32, 0x03, 0xA8, 0x10, 0x12, 0xD9, 0x95, 0x50, 0xA5, 0x2E, + 0xC0, 0xBD, 0xDF, 0x56, 0x78, 0x36, 0xB7, 0x04, 0x4F, 0x2E, 0x21, 0xA8, 0x80, 0xAB, 0x7E, 0x3D, + 0xED, 0xF5, 0x7A, 0xD0, 0xD7, 0x27, 0x65, 0x47, 0x7D, 0xB7, 0x40, 0x72, 0x6D, 0x4E, 0x08, 0x92, + 0x0D, 0x69, 0x77, 0x51, 0xB2, 0x01, 0xA9, 0x3E, 0xDA, 0x0A, 0xCB, 0x46, 0xCD, 0x03, 0xDA, 0xD8, + 0x30, 0x08, 0xBF, 0xF0, 0x49, 0xDC, 0xE0, 0x15, 0x7A, 0xE0, 0xF6, 0x1A, 0x17, 0x51, 0x6C, 0x10, + 0x55, 0x0B, 0xC8, 0xAE, 0x58, 0x7D, 0xC5, 0xA5, 0xE5, 0x00, 0xE7, 0x2E, 0x6E, 0x1D, 0x46, 0x18, + 0x5D, 0x00, 0xD6, 0x41, 0xD3, 0xD6, 0x41, 0xA8, 0x3D, 0x30, 0x27, 0x16, 0x4A, 0x7F, 0x47, 0x11, + 0x46, 0xC7, 0x44, 0x5E, 0xC0, 0x15, 0x50, 0xBC, 0xB6, 0xD1, 0x26, 0x81, 0xCA, 0x76, 0xC1, 0x3C, + 0x58, 0x1C, 0x27, 0xF6, 0x6C, 0xDE, 0x17, 0x8C, 0xB1, 0x60, 0x77, 0x68, 0x46, 0x7A, 0xB8, 0x39, + 0xC7, 0x21, 0xC2, 0xE2, 0x62, 0xF2, 0xC1, 0x76, 0xA9, 0x9F, 0xBA, 0x69, 0x46, 0x0F, 0x75, 0xA7, + 0x6B, 0x8A, 0x2B, 0x40, 0xEC, 0xC2, 0x42, 0xD7, 0x16, 0xE8, 0x34, 0x90, 0x4C, 0x71, 0x33, 0xBA, + 0xBA, 0xD0, 0x7B, 0x6D, 0xF4, 0xC9, 0x32, 0x23, 0x52, 0xB0, 0x93, 0x5E, 0xFF, 0xEB, 0xA9, 0x0B, + 0x46, 0xA1, 0x6B, 0x77, 0xC1, 0xBE, 0xA0, 0x5D, 0x91, 0xF6, 0x91, 0x3E, 0xC4, 0x51, 0x07, 0x57, + 0x21, 0xE6, 0xD3, 0x7C, 0x07, 0xA6, 0x22, 0x27, 0x70, 0x5B, 0x16, 0xD8, 0x14, 0xDC, 0x9A, 0xA3, + 0x38, 0x24, 0x27, 0x96, 0xC5, 0x1D, 0x64, 0x8A, 0xD5, 0x0D, 0x01, 0x16, 0x58, 0x16, 0x58, 0x14, + 0xE9, 0x8F, 0x28, 0x22, 0xE2, 0x80, 0x45, 0x47, 0xDE, 0x92, 0x1D, 0x45, 0x53, 0x0A, 0x66, 0x03, + 0xD0, 0x81, 0x45, 0x09, 0x5B, 0xC3, 0x06, 0x52, 0x9B, 0xDC, 0x70, 0x6C, 0xA4, 0xE7, 0x93, 0xCC, + 0xC0, 0x09, 0x26, 0x4A, 0x53, 0xC8, 0x76, 0x9E, 0x63, 0x07, 0xF6, 0x7A, 0xD2, 0xD6, 0x2A, 0xF8, + 0x12, 0xFC, 0x35, 0xB4, 0xDD, 0xEA, 0x76, 0xAF, 0x91, 0x73, 0x26, 0xC8, 0x1C, 0x10, 0xCE, 0x6D, + 0xA3, 0x70, 0xF4, 0x5C, 0x21, 0x7A, 0x60, 0x54, 0x6D, 0x87, 0x44, 0x0E, 0xB9, 0xD5, 0x16, 0xAA, + 0xE8, 0x72, 0xA3, 0x74, 0x5B, 0x1A, 0x60, 0x94, 0xE6, 0x19, 0x05, 0x34, 0xBA, 0x15, 0x77, 0x62, + 0x00, 0xF5, 0xE3, 0x3C, 0x39, 0x8D, 0xA0, 0x21, 0xB0, 0xFC, 0xA6, 0xE8, 0x2C, 0x66, 0x64, 0x50, + 0xBE, 0x9E, 0xB6, 0x41, 0x73, 0xFA, 0x2E, 0x78, 0x34, 0x9B, 0x9C, 0x03, 0xA8, 0x8F, 0xD1, 0x96, + 0xF6, 0xDE, 0xB0, 0x51, 0x19, 0x40, 0xC4, 0xA5, 0x84, 0x91, 0x74, 0x09, 0xD7, 0x21, 0xED, 0x0B, + 0x4A, 0x1F, 0x2A, 0x27, 0xE8, 0xA9, 0x4D, 0x97, 0x99, 0xE5, 0x58, 0xD7, 0x0E, 0xC2, 0x51, 0x40, + 0xBC, 0x2C, 0xEB, 0xBA, 0x83, 0x1D, 0x36, 0x29, 0x7D, 0x4F, 0xA0, 0xD4, 0xBB, 0xB6, 0x91, 0xE6, + 0x0E, 0xC1, 0xB2, 0x70, 0x05, 0x8B, 0x6E, 0x6D, 0x58, 0x82, 0x60, 0xC1, 0xC2, 0x5D, 0x8C, 0x0D, + 0x60, 0xAF, 0x28, 0xB9, 0xE0, 0x35, 0x2D, 0x72, 0x5B, 0xA4, 0x63, 0x48, 0x24, 0x92, 0x79, 0xB2, + 0x82, 0xB8, 0x28, 0x81, 0x30, 0x50, 0x55, 0xAD, 0x2E, 0x12, 0x45, 0x68, 0x22, 0x1A, 0x38, 0x52, + 0x0D, 0xE8, 0x03, 0xFC, 0x5D, 0xD4, 0x19, 0x40, 0x58, 0xA1, 0x46, 0xC4, 0x9E, 0x13, 0x52, 0x06, + 0x0C, 0x9F, 0x59, 0x10, 0x8D, 0x08, 0x9E, 0x29, 0x3D, 0xEE, 0x10, 0xA6, 0x0E, 0x9A, 0x0B, 0xB7, + 0xC7, 0x6D, 0x5C, 0xAB, 0x8B, 0x34, 0x04, 0x53, 0x6E, 0x02, 0x7A, 0x68, 0x0B, 0x80, 0xC1, 0x3D, + 0x47, 0xE1, 0x64, 0x23, 0xC0, 0x02, 0x02, 0x85, 0x70, 0x94, 0x43, 0xE4, 0xEF, 0x80, 0x3A, 0xF5, + 0xB0, 0x05, 0x2D, 0x0F, 0xF8, 0xDE, 0x2E, 0x80, 0x31, 0xCD, 0x19, 0x60, 0x63, 0x02, 0x06, 0x26, + 0x6D, 0xA4, 0x9B, 0xE3, 0x2F, 0x8C, 0x12, 0x7C, 0xDF, 0xD0, 0x00, 0xDA, 0x8D, 0x92, 0x37, 0xF2, + 0x7C, 0xE4, 0x0C, 0x7B, 0x69, 0x36, 0x35, 0x61, 0x5F, 0x97, 0x94, 0x18, 0x66, 0x8A, 0x89, 0x96, + 0x49, 0x03, 0xA9, 0x49, 0x5A, 0x3B, 0xF8, 0x3C, 0x49, 0x3B, 0x7F, 0x65, 0xE1, 0x62, 0x87, 0x62, + 0x2A, 0x14, 0x42, 0x8F, 0xD4, 0x22, 0xAE, 0x56, 0x8B, 0xBE, 0x38, 0x9A, 0x84, 0xC1, 0xE4, 0xCB, + 0x48, 0x3D, 0x7B, 0x5B, 0xD7, 0x86, 0xAA, 0x12, 0x80, 0x2A, 0x84, 0x31, 0x64, 0x66, 0x01, 0x84, + 0xE3, 0xEA, 0xB6, 0x4E, 0x57, 0xB5, 0xB2, 0xE9, 0x54, 0xF4, 0xB2, 0x69, 0x7F, 0x77, 0xCD, 0x9C, + 0x06, 0x61, 0x28, 0x37, 0xA9, 0x92, 0x9A, 0xF6, 0x31, 0x18, 0x32, 0xCD, 0x1B, 0x9B, 0xB8, 0xD9, + 0x93, 0x16, 0x1C, 0xE2, 0x3C, 0x11, 0xDA, 0x90, 0x84, 0x63, 0xCB, 0xCC, 0x80, 0x95, 0x21, 0x62, + 0xB2, 0x89, 0x63, 0xAE, 0x25, 0x3C, 0x68, 0x9B, 0x42, 0x62, 0xEB, 0x06, 0x04, 0x0F, 0x79, 0x8A, + 0x23, 0x5C, 0x12, 0xCE, 0xAE, 0x70, 0xEE, 0x7D, 0x12, 0x45, 0x21, 0x9F, 0x26, 0x72, 0xB5, 0x43, + 0xCB, 0xE0, 0xA0, 0xA2, 0x95, 0x17, 0x83, 0x67, 0x80, 0xCE, 0x35, 0x41, 0xA0, 0x36, 0x9A, 0x2F, + 0x44, 0x0E, 0x67, 0x8B, 0xC9, 0xB8, 0x76, 0xDE, 0xC6, 0x8D, 0x7C, 0x20, 0xAD, 0x0F, 0xAE, 0x41, + 0x6E, 0x48, 0x88, 0x81, 0x6D, 0xA1, 0x35, 0x77, 0x50, 0x83, 0x51, 0xFF, 0x40, 0x06, 0x67, 0x80, + 0xAA, 0x22, 0x55, 0x0E, 0xE5, 0x8A, 0xEC, 0x00, 0x29, 0x86, 0x94, 0x3A, 0xDA, 0xE9, 0x57, 0xB5, + 0x55, 0x12, 0x91, 0x5D, 0xEC, 0x2F, 0x55, 0xD3, 0x28, 0xE3, 0x43, 0x56, 0xA2, 0x1C, 0x21, 0xC7, + 0xDF, 0x7C, 0xFC, 0xCF, 0xD7, 0x67, 0xE7, 0x27, 0xEF, 0x7E, 0x53, 0x77, 0x88, 0x55, 0x2E, 0x52, + 0x08, 0xAF, 0x85, 0x69, 0x53, 0x4B, 0x1C, 0x74, 0x38, 0x6A, 0x41, 0xAA, 0xB5, 0x33, 0xDF, 0x12, + 0xE5, 0xBA, 0xE3, 0xA3, 0x99, 0x4D, 0xE0, 0x4F, 0xCF, 0xFF, 0x8E, 0x60, 0x66, 0x36, 0x7C, 0x65, + 0x5D, 0xBB, 0xE7, 0x2A, 0x32, 0xF3, 0x14, 0x82, 0xF8, 0xE6, 0xE4, 0xED, 0xEB, 0xF3, 0x7F, 0x9C, + 0x5F, 0xBC, 0x3E, 0x55, 0xB7, 0x87, 0x66, 0x6F, 0xD7, 0x21, 0x38, 0x85, 0xD6, 0x99, 0xF2, 0x26, + 0x08, 0x59, 0x7A, 0x97, 0x72, 0x36, 0xDF, 0x03, 0x9B, 0xB2, 0x73, 0x00, 0x44, 0xE5, 0x4B, 0x85, + 0xCA, 0x97, 0x2A, 0x16, 0x2C, 0xC5, 0x5A, 0x54, 0xBA, 0x14, 0x35, 0x34, 0x55, 0x89, 0xBC, 0x39, + 0x74, 0xCE, 0xEF, 0xB0, 0x31, 0xFD, 0xF4, 0x59, 0x55, 0xE6, 0xCB, 0x90, 0x07, 0x0B, 0x24, 0x63, + 0x76, 0xA7, 0x82, 0x1E, 0x0A, 0x48, 0x85, 0x82, 0x28, 0xA5, 0x97, 0x63, 0xAA, 0x5C, 0x41, 0x94, + 0x41, 0xC5, 0x1A, 0x95, 0xCA, 0xA8, 0x5A, 0xE8, 0xDE, 0x39, 0x8B, 0x7C, 0x5C, 0x8A, 0x34, 0xF0, + 0xC6, 0x0B, 0x97, 0x30, 0xEF, 0x03, 0x8D, 0x55, 0x8F, 0x5F, 0x44, 0xE3, 0x74, 0x31, 0x14, 0xDF, + 0x47, 0x8B, 0x24, 0xBE, 0x4A, 0x58, 0x9A, 0x66, 0x3C, 0xBD, 0x09, 0xD2, 0x60, 0x1C, 0x84, 0x01, + 0xBF, 0x1B, 0x00, 0xE1, 0x7C, 0x16, 0x65, 0xA8, 0x2F, 0x92, 0x2B, 0xB1, 0x24, 0xDD, 0x40, 0xC6, + 0x4D, 0x69, 0x2F, 0x19, 0x13, 0x09, 0x02, 0xB2, 0xE5, 0x44, 0x7C, 0x76, 0xF0, 0x6F, 0x1F, 0xE9, + 0x24, 0xDF, 0x45, 0xEA, 0x9C, 0x59, 0x01, 0xB2, 0x27, 0x4F, 0x21, 0x45, 0x65, 0xDF, 0xBF, 0xC4, + 0xF3, 0xB9, 0x17, 0xF9, 0xF5, 0x5A, 0x18, 0xA4, 0xBC, 0xA6, 0xD7, 0xBC, 0x30, 0xAC, 0x95, 0xC8, + 0x70, 0xC6, 0xA6, 0x80, 0xED, 0xAC, 0x64, 0xB1, 0xCA, 0xAB, 0x22, 0x9E, 0x39, 0xB4, 0x5F, 0x12, + 0x06, 0xE6, 0xC4, 0x0F, 0x92, 0xBA, 0xA6, 0x1E, 0xB2, 0x5A, 0xAE, 0x59, 0x98, 0x2C, 0xBC, 0xAF, + 0xD8, 0x2B, 0x17, 0xFF, 0x87, 0xF1, 0x09, 0xC8, 0x81, 0x02, 0x6D, 0x6D, 0x55, 0xB9, 0x43, 0xDA, + 0xA9, 0xD9, 0x6C, 0xA7, 0x34, 0xDB, 0x86, 0xFB, 0x04, 0x06, 0xD9, 0x70, 0xB9, 0xA3, 0x8B, 0x30, + 0x57, 0xB2, 0xC4, 0x8A, 0x2A, 0x99, 0xC1, 0xC1, 0xA1, 0x77, 0x04, 0x2E, 0xB3, 0x9D, 0xED, 0x92, + 0xE1, 0x6C, 0x3F, 0x08, 0x07, 0xB5, 0x17, 0xE1, 0x58, 0x02, 0x21, 0x1B, 0x2E, 0x79, 0x61, 0x19, + 0x5A, 0x7B, 0xF2, 0x71, 0x25, 0x21, 0x82, 0x41, 0xC9, 0x80, 0x50, 0x8D, 0x5A, 0x3D, 0x6E, 0x00, + 0x01, 0x01, 0x46, 0x6E, 0x20, 0x48, 0x45, 0x36, 0x68, 0x2A, 0x7D, 0x03, 0x52, 0x95, 0x64, 0x07, + 0x0C, 0x70, 0x4E, 0xC9, 0x20, 0x9A, 0xC6, 0x99, 0x34, 0x96, 0x67, 0x57, 0x0C, 0x82, 0xA8, 0xB3, + 0xC8, 0x19, 0xE2, 0xA1, 0x72, 0x60, 0x49, 0xCD, 0x04, 0xB7, 0xA8, 0xAA, 0xA3, 0x54, 0x89, 0xEA, + 0x0D, 0x49, 0xD5, 0xAC, 0xCC, 0xDF, 0x0B, 0x10, 0x1B, 0x80, 0x3D, 0xC3, 0xF6, 0xE3, 0xDF, 0x40, + 0xB0, 0xF3, 0x87, 0x73, 0xD8, 0x76, 0xF6, 0x20, 0x4C, 0xC5, 0xF9, 0x25, 0x0F, 0xE6, 0xB0, 0xCF, + 0x8B, 0xA0, 0x18, 0x56, 0x91, 0x95, 0x8D, 0xB6, 0xDC, 0x0F, 0xCE, 0xF2, 0x3D, 0x48, 0x34, 0x50, + 0xD6, 0x73, 0x93, 0x70, 0x89, 0x62, 0x49, 0xE3, 0x44, 0x71, 0xE9, 0xB0, 0xE9, 0x93, 0x25, 0x3E, + 0xA1, 0x7A, 0x29, 0xF7, 0xF8, 0x32, 0x55, 0x73, 0x5A, 0x6F, 0x7D, 0x3F, 0x60, 0xFC, 0x3E, 0x7E, + 0x78, 0xFF, 0xEA, 0xA7, 0x8B, 0xD7, 0x87, 0x4D, 0x9F, 0xCC, 0xCB, 0x95, 0x0F, 0x0B, 0x1F, 0x84, + 0xFF, 0x01, 0xCB, 0x57, 0x51, 0xDF, 0xBD, 0x86, 0x70, 0xB5, 0xD7, 0x0C, 0x96, 0x62, 0xFD, 0x27, + 0x9B, 0x3E, 0x78, 0x28, 0x69, 0xBE, 0xB0, 0x6E, 0xDB, 0x36, 0x0F, 0x37, 0x51, 0x5E, 0xE6, 0x29, + 0x06, 0x6F, 0xBA, 0xCA, 0x4D, 0x1E, 0xDE, 0xEE, 0x36, 0x7A, 0x39, 0xE4, 0xDC, 0xF7, 0xCD, 0xD3, + 0x2B, 0x75, 0x3F, 0xF8, 0xE3, 0x33, 0x06, 0x7C, 0x4C, 0x38, 0x50, 0x5B, 0x57, 0xC0, 0xEC, 0x7B, + 0x29, 0x53, 0x56, 0x5E, 0xC0, 0x9B, 0xF0, 0x5F, 0xE6, 0x18, 0x73, 0x50, 0x93, 0x78, 0x89, 0xCE, + 0xED, 0x61, 0x97, 0x59, 0xB0, 0x29, 0x8F, 0xB7, 0xB0, 0x9E, 0x99, 0x2B, 0x1B, 0x15, 0x44, 0xAB, + 0x8C, 0xAF, 0xD4, 0x48, 0x77, 0x75, 0x89, 0xD2, 0x38, 0xF4, 0xCC, 0x9C, 0xE3, 0x13, 0x40, 0x9D, + 0x07, 0xD3, 0x60, 0x42, 0x2F, 0xBA, 0xC0, 0xF3, 0x3A, 0x3B, 0x64, 0xAE, 0x28, 0x59, 0xCB, 0x50, + 0xE0, 0xB8, 0x12, 0x58, 0x8A, 0x6E, 0xB4, 0x19, 0xAA, 0x92, 0x07, 0x6B, 0xC7, 0x1F, 0x52, 0x50, + 0x5B, 0xB9, 0xBD, 0x0D, 0x07, 0x58, 0x3E, 0xD7, 0x94, 0x89, 0x80, 0x98, 0x4E, 0x9B, 0x44, 0xFE, + 0x57, 0x22, 0x51, 0x2C, 0xFD, 0x17, 0xA4, 0x9A, 0x25, 0x8F, 0x47, 0xE2, 0x3D, 0xF4, 0xAD, 0xC0, + 0x82, 0x3C, 0x01, 0x91, 0x85, 0x9C, 0x22, 0x91, 0x59, 0x1C, 0x46, 0x66, 0xBC, 0x2B, 0x40, 0x29, + 0x17, 0xF1, 0x37, 0xC2, 0x88, 0x4C, 0xD2, 0xF7, 0xE9, 0x43, 0xE1, 0xF1, 0xDE, 0x9E, 0xA1, 0x77, + 0x92, 0xF2, 0x7E, 0xBE, 0x1C, 0xCF, 0x03, 0xBE, 0xD3, 0x42, 0xA4, 0x13, 0x30, 0x98, 0xFC, 0xF8, + 0xC6, 0x4B, 0x94, 0x55, 0x7A, 0x99, 0xC6, 0xCB, 0x64, 0xC2, 0xF4, 0xDB, 0x79, 0x88, 0xC9, 0xB4, + 0x08, 0x23, 0xF4, 0xC9, 0x32, 0xC1, 0x97, 0x90, 0x68, 0xA5, 0x47, 0x6A, 0x4B, 0xD5, 0x61, 0x0B, + 0x33, 0x64, 0xBB, 0x60, 0xFA, 0xE8, 0x07, 0x4B, 0x5F, 0xB1, 0x71, 0x1A, 0x4F, 0xBE, 0x30, 0x7E, + 0xB9, 0x88, 0x13, 0x3E, 0x32, 0x4B, 0x0D, 0x27, 0xEF, 0x47, 0x2A, 0x4C, 0x49, 0xEF, 0xA2, 0xC9, + 0x25, 0xB4, 0x42, 0x52, 0x3E, 0x5F, 0x46, 0xA5, 0xA9, 0x28, 0x8E, 0x97, 0x48, 0x2A, 0x55, 0x07, + 0xF1, 0xBC, 0x8C, 0xA7, 0xD3, 0x2A, 0x40, 0x52, 0x0A, 0xE6, 0x63, 0x23, 0x4B, 0x17, 0x97, 0x2C, + 0x49, 0xE2, 0xE4, 0x72, 0x0E, 0x2A, 0x06, 0xF3, 0x70, 0x52, 0xD1, 0x38, 0x89, 0x7D, 0x06, 0x4B, + 0x23, 0xA1, 0x04, 0xE2, 0x23, 0x73, 0x38, 0x5D, 0x46, 0xF4, 0x3E, 0x16, 0x94, 0xF7, 0x66, 0xEC, + 0x81, 0xC3, 0xBE, 0xC7, 0x8D, 0xC2, 0xC4, 0x92, 0x89, 0x52, 0x75, 0x3E, 0x2A, 0x6D, 0xB1, 0x99, + 0x2E, 0x40, 0x2F, 0xEB, 0xB0, 0x51, 0x4D, 0x8F, 0x68, 0xBF, 0xC1, 0xC8, 0x1A, 0x02, 0xAB, 0xEB, + 0xAC, 0x81, 0xF3, 0x7C, 0x49, 0xFC, 0x9A, 0x70, 0xF9, 0x35, 0x25, 0x27, 0xFA, 0xEF, 0x6A, 0x99, + 0x52, 0xB5, 0x56, 0x6D, 0xA8, 0xEC, 0x0F, 0x3C, 0x7E, 0x57, 0x8F, 0x5B, 0xA4, 0xB6, 0xEA, 0x30, + 0x38, 0xE2, 0xCD, 0x90, 0x45, 0x57, 0x1C, 0x02, 0xF5, 0xA1, 0xB6, 0x67, 0x95, 0x3D, 0x8B, 0xA8, + 0x8D, 0x7A, 0xD4, 0x18, 0xF1, 0x4F, 0xC1, 0xE7, 0x06, 0x62, 0xDC, 0x50, 0x1F, 0x5A, 0x54, 0x6D, + 0x88, 0xC1, 0xB9, 0x81, 0x92, 0x58, 0xE8, 0x41, 0xA3, 0x31, 0x4C, 0x18, 0x5F, 0x26, 0x91, 0x42, + 0x28, 0x94, 0xAD, 0x89, 0xBA, 0xCE, 0x09, 0x09, 0xCA, 0x91, 0xCE, 0x2E, 0x31, 0x69, 0x03, 0x62, + 0x8A, 0xF1, 0x6A, 0x16, 0x7C, 0xD4, 0xDA, 0x76, 0x0D, 0x82, 0x86, 0x9A, 0x05, 0x17, 0x08, 0x33, + 0x6A, 0x9D, 0x1A, 0x86, 0x19, 0x78, 0x11, 0xBE, 0xB0, 0x66, 0xB7, 0x6B, 0x59, 0x2C, 0x52, 0xEB, + 0xD6, 0xA4, 0x62, 0xD4, 0x30, 0x7C, 0x18, 0x24, 0xCC, 0x1F, 0xD6, 0x94, 0x16, 0x20, 0xB2, 0x0D, + 0x6E, 0x37, 0x00, 0xBB, 0x0A, 0x80, 0xC2, 0x8F, 0x2D, 0x10, 0x8E, 0x29, 0x40, 0xF4, 0xF6, 0x60, + 0xD4, 0xE9, 0x16, 0x00, 0xC1, 0x86, 0x3F, 0x8C, 0x93, 0x5D, 0x05, 0x68, 0x99, 0x02, 0x22, 0x5E, + 0x25, 0xC8, 0x5E, 0x19, 0xA4, 0xFB, 0x68, 0x88, 0x76, 0x7F, 0x27, 0x04, 0xE7, 0x31, 0xBB, 0x74, + 0x05, 0x08, 0xD7, 0x11, 0x48, 0x75, 0x05, 0x4E, 0xDD, 0x1C, 0x60, 0x09, 0x5E, 0xE7, 0x51, 0x00, + 0x3B, 0xDF, 0x1B, 0x60, 0xEF, 0x7B, 0x00, 0x14, 0x21, 0x25, 0x82, 0x2D, 0xA2, 0xEC, 0x9A, 0xED, + 0x96, 0x44, 0x02, 0xEE, 0xB3, 0x28, 0xBB, 0x46, 0x55, 0x01, 0x1B, 0x4B, 0xC5, 0xBD, 0xDA, 0xF1, + 0xF7, 0x14, 0xD1, 0x3F, 0x2A, 0x9F, 0xDF, 0x57, 0x38, 0xBF, 0xB3, 0x64, 0xFE, 0x51, 0xB1, 0xFC, + 0xBE, 0x32, 0xF9, 0x7D, 0x05, 0xF2, 0x9F, 0x22, 0x8D, 0x85, 0x69, 0xC4, 0x57, 0xE8, 0x9B, 0x96, + 0xF1, 0xB1, 0x82, 0x6A, 0xBB, 0xF0, 0x7F, 0x2D, 0xAF, 0x12, 0xD7, 0x4E, 0xBB, 0xBA, 0xA3, 0xBC, + 0xB5, 0xF5, 0x9E, 0xF2, 0xB6, 0xAB, 0x5B, 0x0E, 0x7D, 0x9B, 0xCA, 0x5B, 0x4B, 0x5E, 0x7A, 0xBA, + 0x65, 0x89, 0x4B, 0x5B, 0x34, 0x76, 0xE0, 0x62, 0xD2, 0xA5, 0xAF, 0x5B, 0x5D, 0xFA, 0xEE, 0x53, + 0x93, 0x0D, 0xC3, 0x6D, 0x79, 0xB1, 0x75, 0xAB, 0x47, 0x97, 0x1E, 0xB5, 0x75, 0x10, 0x6A, 0x47, + 0xF9, 0x8A, 0x1B, 0x4C, 0xE2, 0x2F, 0xB0, 0x43, 0x2A, 0xC6, 0xD4, 0x44, 0x3E, 0x57, 0xA3, 0x9D, + 0xEE, 0xDC, 0xA8, 0x08, 0xDB, 0x2F, 0x31, 0x03, 0x66, 0xDA, 0x7D, 0xC9, 0x1F, 0x35, 0x46, 0x0C, + 0xDD, 0x90, 0x5E, 0xF6, 0x40, 0x2A, 0x25, 0x36, 0xBA, 0x0A, 0x1E, 0x48, 0xD5, 0x0A, 0x18, 0x10, + 0x08, 0xE0, 0xD9, 0xA6, 0x73, 0x8E, 0x05, 0xFA, 0xB4, 0xCE, 0x74, 0x9E, 0x11, 0xAD, 0xCE, 0x46, + 0xAC, 0xC9, 0xE3, 0xB7, 0xF1, 0x8A, 0x25, 0xBF, 0x40, 0x18, 0x5C, 0xD7, 0xB4, 0xA3, 0x3A, 0x1F, + 0xF1, 0x8D, 0xB6, 0x97, 0x86, 0x35, 0xE0, 0x47, 0xEC, 0xA5, 0x35, 0x30, 0x0B, 0xA8, 0x78, 0x8E, + 0xC0, 0xE3, 0x93, 0x19, 0x25, 0x12, 0x94, 0x16, 0x21, 0x86, 0xE8, 0xF3, 0x39, 0x06, 0x0B, 0x18, + 0x71, 0x0C, 0x83, 0x29, 0x40, 0x53, 0xCB, 0xE5, 0x8E, 0x73, 0x1A, 0x39, 0x50, 0xD4, 0x06, 0x6B, + 0x8A, 0x59, 0x3A, 0x6F, 0x54, 0x87, 0x7C, 0x2B, 0x3F, 0x5C, 0xC4, 0xDC, 0x0B, 0x15, 0x71, 0x1A, + 0x8B, 0x26, 0x71, 0x6C, 0x38, 0x3C, 0x07, 0xE2, 0x59, 0xBF, 0x3C, 0x65, 0x09, 0xCF, 0x87, 0x67, + 0xBC, 0x9B, 0x4C, 0x96, 0x0B, 0xF1, 0x3B, 0x18, 0x45, 0xA5, 0xA1, 0x47, 0x73, 0x06, 0x21, 0xA1, + 0x32, 0x0F, 0x22, 0x10, 0x9A, 0x1A, 0x25, 0x1E, 0xC2, 0x2E, 0xCC, 0x40, 0xAA, 0x46, 0xB5, 0x3E, + 0xDC, 0x89, 0x80, 0xAF, 0x86, 0x2B, 0xC4, 0xF9, 0x7C, 0x88, 0x0A, 0x80, 0x89, 0x34, 0x59, 0x26, + 0xD6, 0x9B, 0xFD, 0x7F, 0x53, 0x75, 0x3F, 0x9E, 0x2C, 0xE7, 0xC0, 0xC7, 0xE6, 0x15, 0xE3, 0xAF, + 0x43, 0x86, 0xB7, 0x3F, 0xDF, 0x9D, 0x00, 0xFF, 0x64, 0x7A, 0xA9, 0x35, 0x83, 0x28, 0x62, 0xC9, + 0xAF, 0x17, 0xA7, 0x6F, 0x47, 0x5C, 0x27, 0x72, 0x02, 0xAB, 0x7F, 0x28, 0x87, 0x4E, 0x82, 0xD2, + 0x41, 0x25, 0x9A, 0x82, 0x28, 0x86, 0x9F, 0xE0, 0x09, 0xA7, 0x77, 0x53, 0x8C, 0xA9, 0xF4, 0x4A, + 0x9F, 0x08, 0x7A, 0x6C, 0x6D, 0x48, 0xBB, 0xE3, 0x49, 0xA6, 0x69, 0xE5, 0x13, 0xCB, 0x07, 0x42, + 0x9F, 0x4A, 0xD4, 0x06, 0x43, 0x58, 0xDD, 0x84, 0x30, 0xC6, 0x7A, 0x44, 0x14, 0x84, 0x01, 0x16, + 0x84, 0x42, 0x25, 0x8D, 0x2D, 0x22, 0x22, 0x10, 0xCD, 0x10, 0xA3, 0x73, 0xB0, 0x0F, 0xB5, 0x63, + 0xC8, 0x77, 0x31, 0xFF, 0xCA, 0xD2, 0x2B, 0x75, 0xCD, 0x9A, 0x24, 0x5B, 0x4D, 0xC0, 0x8F, 0xD7, + 0x33, 0xB9, 0x2B, 0x8B, 0xEF, 0x96, 0x64, 0x37, 0x31, 0x71, 0xD4, 0x39, 0x5D, 0xB4, 0xB5, 0x86, + 0x54, 0x1B, 0x95, 0x49, 0xF4, 0xE2, 0x45, 0x1D, 0xE4, 0xD2, 0xD4, 0x28, 0xC6, 0x44, 0x02, 0x86, + 0x18, 0xEC, 0xC6, 0x10, 0xBD, 0xC6, 0x47, 0xD9, 0x6A, 0x82, 0x52, 0xC3, 0xB8, 0xD1, 0xD0, 0x54, + 0xC3, 0x02, 0xAA, 0x0B, 0xE8, 0x75, 0xD9, 0xFF, 0x29, 0xFE, 0xDC, 0xC4, 0x02, 0x8D, 0x06, 0xB0, + 0x88, 0x94, 0x17, 0x67, 0xC7, 0x52, 0x64, 0x28, 0x05, 0x05, 0x23, 0x54, 0xB6, 0x3C, 0x25, 0x83, + 0xB4, 0xC3, 0x08, 0x29, 0xC7, 0x0A, 0xFC, 0x57, 0x58, 0x22, 0x4B, 0xB7, 0xC1, 0x92, 0xE8, 0xB6, + 0x85, 0xF6, 0xC8, 0xC6, 0xFB, 0x8E, 0xB8, 0x74, 0xA9, 0xCD, 0x42, 0x1B, 0xF2, 0xD6, 0xB2, 0xE5, + 0xB7, 0xA5, 0xE0, 0x30, 0xEB, 0x11, 0x56, 0x05, 0x0F, 0x28, 0x2A, 0xB7, 0x96, 0xF0, 0xC5, 0x77, + 0x78, 0xAD, 0x29, 0xB7, 0x36, 0x5C, 0xC0, 0xFA, 0xDE, 0xD9, 0xE4, 0x07, 0x37, 0x20, 0x88, 0x47, + 0x43, 0xA2, 0x6F, 0xD5, 0x5A, 0xD9, 0x26, 0x65, 0xA9, 0x89, 0xE2, 0x58, 0x68, 0xA8, 0x1D, 0x5D, + 0xBC, 0x92, 0xF1, 0xF3, 0xEF, 0x32, 0x80, 0xFE, 0x3D, 0xB3, 0xE2, 0x6A, 0x7E, 0x06, 0x6C, 0x71, + 0x3B, 0xA4, 0xF7, 0x0E, 0xE2, 0xE5, 0x61, 0x0D, 0x34, 0x82, 0x4C, 0x58, 0x89, 0xA4, 0xC8, 0xB1, + 0x46, 0x2D, 0x7F, 0x61, 0x28, 0xDE, 0x17, 0x56, 0x52, 0xB5, 0x52, 0xF5, 0xBA, 0x86, 0x2B, 0x6F, + 0x4C, 0xD6, 0x55, 0x3A, 0xDC, 0xD7, 0xC4, 0x1F, 0x4D, 0x36, 0xAF, 0xBE, 0x02, 0xDF, 0x36, 0x06, + 0xBC, 0x78, 0x51, 0x1A, 0xB1, 0xDD, 0xFD, 0xED, 0x1B, 0x8A, 0x86, 0xA5, 0xC9, 0x5D, 0x8A, 0xB4, + 0x0D, 0x2B, 0xEE, 0x17, 0xAF, 0x8E, 0x61, 0x8F, 0x62, 0xB7, 0x1B, 0x32, 0x20, 0xC7, 0x52, 0x6F, + 0xA9, 0x6B, 0xE6, 0xA5, 0xEF, 0x56, 0xD1, 0xFB, 0x24, 0x5E, 0xB0, 0x84, 0xDF, 0xD5, 0x55, 0x2A, + 0x62, 0x69, 0x2F, 0xEB, 0x20, 0x6C, 0xA6, 0x98, 0xB2, 0x03, 0x1E, 0x0E, 0x2A, 0xC1, 0xD3, 0x06, + 0xD9, 0x40, 0x09, 0x5F, 0x3E, 0x66, 0x02, 0x65, 0xFE, 0xAD, 0x56, 0xA6, 0xCE, 0xEF, 0xB2, 0x2A, + 0xFA, 0xBB, 0x5A, 0x52, 0xE1, 0x57, 0xE0, 0x3B, 0x38, 0xAB, 0x93, 0x85, 0xAA, 0x12, 0x5A, 0xAD, + 0x69, 0x98, 0x9F, 0x20, 0xD0, 0x72, 0x92, 0x51, 0xD9, 0x3B, 0x2E, 0x2D, 0x74, 0x11, 0xBF, 0x50, + 0xCA, 0x0B, 0xB5, 0x49, 0x40, 0x63, 0x92, 0x4D, 0x8D, 0x49, 0xA4, 0xC6, 0x8C, 0x36, 0x35, 0x26, + 0xD9, 0xD6, 0x98, 0x67, 0xE9, 0x4A, 0x49, 0x4F, 0xFA, 0xC2, 0x29, 0xF7, 0xD1, 0xBD, 0x82, 0x6B, + 0x06, 0x2F, 0x2C, 0xBF, 0xDA, 0xE8, 0x68, 0x5D, 0xD4, 0x0B, 0x17, 0x35, 0xA9, 0x4D, 0xEA, 0x64, + 0xD3, 0x50, 0xBC, 0xA0, 0x7B, 0x46, 0xE5, 0x72, 0x68, 0x7E, 0x9B, 0xBE, 0x6D, 0xA1, 0x5B, 0xD0, + 0xFF, 0x38, 0xEF, 0x5C, 0x08, 0x3F, 0x31, 0xA4, 0x9A, 0x3D, 0x2A, 0xB9, 0x98, 0xE6, 0xB1, 0xCC, + 0xF6, 0xF1, 0xD7, 0xB2, 0x9D, 0x2D, 0x39, 0xF8, 0x12, 0xA3, 0x92, 0x82, 0x51, 0xC3, 0x8C, 0x53, + 0x1B, 0x7D, 0x85, 0xB0, 0xE4, 0x52, 0x52, 0x12, 0x97, 0xD2, 0xE0, 0xDD, 0xE2, 0xA8, 0x97, 0xA5, + 0xF1, 0xE9, 0x52, 0xB5, 0x1F, 0xDF, 0x67, 0x09, 0xD6, 0x5E, 0x8F, 0x98, 0xD5, 0x80, 0xCB, 0x2E, + 0x31, 0x7C, 0xA9, 0xAA, 0x03, 0x15, 0x8B, 0xC2, 0x07, 0x5C, 0x29, 0xBE, 0x9F, 0x2A, 0x4F, 0x8A, + 0x5E, 0xAA, 0xF8, 0x36, 0x49, 0xA9, 0x58, 0x09, 0x25, 0x48, 0xC1, 0xC5, 0xA7, 0x69, 0xB9, 0x12, + 0x28, 0x0A, 0x1A, 0x4A, 0xC0, 0x61, 0x8D, 0xCC, 0x64, 0xD5, 0x5A, 0x35, 0x49, 0x12, 0xA5, 0xB6, + 0x51, 0x08, 0xAA, 0x1D, 0xFF, 0x3D, 0x56, 0x78, 0xAC, 0x88, 0x63, 0x8C, 0x41, 0x71, 0x98, 0xC2, + 0x3B, 0x3E, 0x80, 0x5D, 0x51, 0x86, 0xAE, 0xFA, 0xFA, 0xBD, 0x13, 0xA8, 0x6E, 0x5F, 0xD9, 0x8F, + 0xAC, 0xB2, 0x14, 0x01, 0x99, 0x54, 0x77, 0x0C, 0x13, 0xE3, 0x68, 0x1A, 0x24, 0xF3, 0xBA, 0xFA, + 0x8B, 0xB8, 0x51, 0x7C, 0xEC, 0xC2, 0x31, 0xF1, 0x14, 0x65, 0x5A, 0xC4, 0x45, 0xA0, 0x8F, 0x95, + 0xE0, 0x91, 0x06, 0x01, 0x49, 0xD9, 0x16, 0xCC, 0x2C, 0xFA, 0x3C, 0x04, 0x16, 0xC6, 0x80, 0x1C, + 0xC7, 0xC9, 0xDD, 0x01, 0xD8, 0x30, 0xA6, 0x0A, 0xBE, 0xF4, 0x7A, 0x47, 0x56, 0x8B, 0x16, 0x09, + 0xB8, 0x74, 0x5E, 0x57, 0x5F, 0x65, 0xE0, 0xA8, 0x16, 0x0C, 0xF1, 0x0F, 0x08, 0x49, 0xB4, 0x0C, + 0x43, 0xB0, 0xDB, 0x1B, 0xA0, 0x27, 0x19, 0x0C, 0x14, 0x7A, 0x30, 0x3C, 0x73, 0x08, 0x58, 0x8B, + 0x15, 0xCA, 0x43, 0x29, 0x72, 0xC0, 0x65, 0xA2, 0x51, 0xC4, 0x56, 0xCA, 0x7F, 0x9D, 0xBE, 0xFD, + 0x95, 0xF3, 0xC5, 0x19, 0xBB, 0x5E, 0x42, 0x00, 0xAB, 0x07, 0x23, 0xB5, 0x45, 0xC2, 0xFC, 0x52, + 0xFC, 0xAE, 0x60, 0x04, 0xDB, 0xD8, 0x2F, 0x97, 0x9B, 0xE2, 0x85, 0x34, 0x89, 0x00, 0x63, 0x90, + 0xA4, 0x66, 0xB3, 0x89, 0x25, 0x1E, 0x08, 0x37, 0x11, 0x9C, 0x28, 0x65, 0x37, 0x58, 0x84, 0x35, + 0xB3, 0x0F, 0x67, 0x27, 0x75, 0xAE, 0x89, 0x4E, 0x51, 0xE3, 0x2B, 0x75, 0x94, 0xA3, 0x3B, 0x3D, + 0x6A, 0xC6, 0x11, 0x6C, 0xCC, 0xBF, 0xC3, 0x90, 0x90, 0x4D, 0x20, 0x3C, 0xBB, 0x62, 0xA3, 0x3C, + 0x06, 0xD2, 0xEE, 0xDD, 0xD1, 0x28, 0x6A, 0xD2, 0x00, 0x8C, 0xA8, 0x81, 0x26, 0x75, 0xDB, 0x34, + 0xB1, 0x4D, 0x84, 0x90, 0x2F, 0x77, 0x44, 0xE7, 0xFF, 0x71, 0xFE, 0xEE, 0x37, 0xF0, 0xBB, 0x09, + 0x84, 0xF4, 0x38, 0x35, 0x5D, 0xC4, 0x51, 0xCA, 0x2E, 0xD8, 0x2D, 0xD7, 0xB4, 0x81, 0x6B, 0x5A, + 0xA5, 0xC9, 0x78, 0x6E, 0x60, 0x50, 0x07, 0x76, 0xA7, 0x71, 0xC8, 0x9A, 0x61, 0x7C, 0x55, 0xCF, + 0xBA, 0x34, 0xFD, 0xCD, 0xC7, 0xD7, 0x58, 0x02, 0x04, 0x22, 0x6B, 0x6B, 0xC4, 0x72, 0xC1, 0xA2, + 0xBA, 0xFA, 0xF7, 0xD7, 0x17, 0xB0, 0x65, 0x1D, 0x22, 0x2B, 0x68, 0x4A, 0x81, 0xE4, 0xF5, 0x0D, + 0x16, 0x88, 0xB7, 0x02, 0x92, 0xC7, 0x07, 0xB5, 0x22, 0x7B, 0x51, 0xA1, 0x09, 0xCB, 0x82, 0x29, + 0x84, 0x89, 0xCE, 0x5A, 0x38, 0x19, 0xED, 0x7E, 0xEF, 0xE4, 0xEA, 0x8B, 0x58, 0xAD, 0x59, 0x79, + 0xE7, 0x9A, 0xB1, 0x65, 0xBF, 0x7E, 0x25, 0x57, 0x30, 0x87, 0x2C, 0x76, 0xB3, 0x78, 0x63, 0x20, + 0xDF, 0x1E, 0x84, 0x4C, 0x1D, 0x8A, 0xC4, 0x06, 0xE5, 0xE6, 0x4D, 0x9C, 0xCC, 0x5F, 0x79, 0xDC, + 0x1B, 0xF2, 0xA6, 0xB7, 0x58, 0xE0, 0x66, 0x85, 0x76, 0x96, 0xE3, 0xED, 0xC2, 0x55, 0x46, 0xE0, + 0x2A, 0xA3, 0xA3, 0x0C, 0xFF, 0x61, 0x04, 0x4E, 0x52, 0x86, 0xEE, 0xEC, 0x53, 0xF4, 0x19, 0xAC, + 0x70, 0x39, 0xA7, 0x0B, 0xA4, 0x15, 0x3D, 0x57, 0x0B, 0xE0, 0xA1, 0x1E, 0x08, 0xF7, 0xA9, 0x17, + 0xEB, 0x15, 0xAF, 0x6F, 0x80, 0xEA, 0xDB, 0x00, 0xB4, 0x75, 0xBD, 0x52, 0x56, 0xDE, 0x21, 0xEE, + 0x9A, 0xE4, 0xDC, 0xFB, 0x77, 0xE7, 0x17, 0x98, 0x5D, 0x10, 0x3C, 0x95, 0x38, 0x58, 0x99, 0xDA, + 0x94, 0x17, 0xF0, 0x61, 0xAF, 0x6F, 0x60, 0x95, 0xB7, 0x60, 0xB3, 0x18, 0x08, 0x3D, 0x52, 0x4C, + 0xBC, 0x7F, 0x51, 0xF5, 0x22, 0x34, 0xD7, 0xEE, 0x81, 0x5D, 0xD9, 0x5E, 0x41, 0xED, 0x16, 0x4B, + 0x2A, 0x6A, 0x66, 0x69, 0x21, 0xCA, 0x92, 0xE7, 0x33, 0xBF, 0x25, 0xD3, 0xB8, 0x7F, 0x83, 0x8C, + 0x6A, 0xBF, 0x8A, 0x09, 0x96, 0x08, 0x36, 0x1E, 0xB0, 0x8C, 0x0F, 0xF1, 0x5D, 0x41, 0xC6, 0x37, + 0x30, 0xA5, 0x7D, 0x83, 0xBF, 0x02, 0xA9, 0x9B, 0x1A, 0xE6, 0x60, 0xEB, 0xB5, 0x4E, 0xC1, 0x5E, + 0x51, 0xC1, 0xB6, 0x36, 0x36, 0x1E, 0x47, 0xD4, 0x5C, 0xD2, 0x39, 0xD2, 0xB0, 0x51, 0x75, 0x94, + 0xD4, 0x98, 0xFA, 0xF3, 0xD0, 0x7B, 0x86, 0x40, 0xCA, 0x77, 0x58, 0x87, 0x5D, 0x4B, 0xA1, 0x44, + 0x72, 0x39, 0x18, 0x7F, 0xD0, 0x20, 0x54, 0x37, 0x55, 0x35, 0x0E, 0xDA, 0x40, 0x34, 0x4B, 0xBD, + 0x5F, 0x6F, 0xD0, 0x89, 0xD4, 0x9D, 0x6B, 0xEB, 0x42, 0xE1, 0x21, 0xE0, 0xF9, 0x6D, 0x39, 0x1F, + 0x83, 0x90, 0x90, 0xC5, 0x2D, 0x34, 0x01, 0xD9, 0x2E, 0x63, 0x43, 0xB0, 0xE6, 0x52, 0x4C, 0x8E, + 0xF8, 0x50, 0x03, 0x63, 0x6B, 0xAA, 0x8D, 0x28, 0x2B, 0x8A, 0x47, 0x05, 0x2C, 0xD8, 0xDD, 0xFB, + 0x5F, 0xD0, 0xD5, 0xE7, 0xD6, 0x03, 0x65, 0x19, 0xD4, 0x8F, 0xE5, 0x15, 0x74, 0xA4, 0xC0, 0x1B, + 0x70, 0x0D, 0xFF, 0x60, 0xE8, 0x0F, 0x1B, 0xAA, 0xA1, 0x36, 0x4A, 0x18, 0x60, 0xEF, 0x69, 0x1C, + 0xF1, 0x19, 0x74, 0x41, 0x8C, 0xB7, 0xB3, 0x1F, 0xC1, 0x41, 0x90, 0xB2, 0xBB, 0xF3, 0xD7, 0x18, + 0x32, 0xE4, 0xBD, 0xBD, 0xA7, 0x41, 0xB4, 0xE4, 0x6C, 0x7F, 0xFF, 0x39, 0x03, 0x33, 0xEA, 0x8B, + 0xFE, 0x62, 0x57, 0xBF, 0x06, 0x3E, 0xFB, 0x29, 0x0C, 0x51, 0x61, 0xF2, 0x37, 0x31, 0xE6, 0xF6, + 0x9B, 0x98, 0x17, 0x2F, 0xF2, 0xF7, 0x44, 0xCD, 0x49, 0x18, 0x63, 0x2D, 0xA6, 0xE0, 0x3B, 0xFD, + 0x4C, 0x62, 0x54, 0x7D, 0x6C, 0xA8, 0x75, 0xE0, 0xF3, 0x44, 0xB8, 0x22, 0xE6, 0x6B, 0x4F, 0x88, + 0x8F, 0xD8, 0xFE, 0xA1, 0xA5, 0x23, 0x3B, 0x99, 0x44, 0xCA, 0x1F, 0x91, 0x8C, 0x54, 0xFC, 0x15, + 0xC9, 0x81, 0x55, 0xF2, 0x17, 0xDE, 0xBB, 0x27, 0x16, 0x14, 0xC9, 0xDD, 0xCA, 0x7D, 0x46, 0x1B, + 0xF5, 0x8D, 0x07, 0xA2, 0xEA, 0x63, 0x6C, 0x55, 0xBC, 0xC9, 0xC2, 0x1F, 0x7C, 0x40, 0x0E, 0xF0, + 0xE6, 0xE3, 0x0F, 0xE5, 0x02, 0xD7, 0x9B, 0x8F, 0xEF, 0xBE, 0xD4, 0x0F, 0xB8, 0x86, 0x7D, 0xCE, + 0x9A, 0x1D, 0x52, 0xC0, 0x43, 0xBB, 0xA6, 0xDF, 0x3A, 0x3E, 0x6B, 0xDB, 0x62, 0x66, 0x81, 0xFA, + 0x49, 0x14, 0xF0, 0x0F, 0x27, 0x52, 0xB8, 0xE3, 0x5D, 0x71, 0x09, 0x68, 0x6F, 0x6B, 0x22, 0x22, + 0x98, 0x97, 0xF2, 0x8A, 0x7A, 0x59, 0x09, 0x1D, 0xD4, 0x4F, 0x10, 0x7E, 0xF6, 0x4C, 0xF3, 0x33, + 0x44, 0x49, 0x7B, 0xDE, 0xFF, 0x6D, 0xBF, 0x49, 0x2C, 0xFB, 0x77, 0x15, 0xF1, 0x50, 0x3E, 0x9C, + 0x40, 0x5E, 0x10, 0x3F, 0x10, 0x79, 0x80, 0x85, 0x87, 0xE0, 0x23, 0x2E, 0x05, 0x1F, 0x99, 0x66, + 0x8A, 0x82, 0x1F, 0x59, 0xC9, 0x38, 0x8B, 0x17, 0xA4, 0xE5, 0x37, 0xF5, 0x88, 0xA6, 0x14, 0x76, + 0x25, 0x7B, 0xF7, 0xF7, 0x23, 0xA0, 0x0C, 0xB3, 0xCA, 0xC8, 0x54, 0x07, 0x62, 0x48, 0x21, 0x2D, + 0x85, 0xA3, 0xC1, 0x22, 0xE6, 0x90, 0x85, 0x29, 0xCB, 0xAD, 0x4A, 0x00, 0xFE, 0x35, 0x38, 0xCA, + 0x86, 0x0C, 0x83, 0xCC, 0xBF, 0x86, 0xA3, 0xE8, 0x53, 0xF0, 0x39, 0x5B, 0x65, 0x00, 0xAB, 0x00, + 0x57, 0x94, 0x1B, 0x96, 0xA4, 0xB0, 0x0D, 0xC8, 0x51, 0xC3, 0x4F, 0xE6, 0x67, 0x19, 0x2E, 0x42, + 0xF0, 0x74, 0x80, 0x93, 0xD9, 0x29, 0xBA, 0x8A, 0x00, 0xDD, 0xA8, 0x8D, 0xF0, 0x93, 0xF5, 0x19, + 0xF2, 0x9A, 0x86, 0xA6, 0xAB, 0x55, 0xDA, 0x6E, 0x01, 0x07, 0x59, 0xA7, 0x36, 0x2B, 0x6B, 0x7B, + 0x59, 0xDF, 0xE6, 0xC6, 0x5E, 0x0C, 0x8A, 0x93, 0x9B, 0x07, 0xDC, 0x01, 0x84, 0x6B, 0x9B, 0x20, + 0xCD, 0xE7, 0x82, 0xCC, 0x42, 0x1E, 0x4D, 0xEE, 0x6E, 0x53, 0x94, 0xB6, 0xF6, 0xB7, 0xF1, 0x4A, + 0x3A, 0xFC, 0x64, 0x67, 0x9D, 0xD5, 0x97, 0xD3, 0xE1, 0x27, 0x27, 0xEF, 0x20, 0x0B, 0x77, 0x4E, + 0x5D, 0xF5, 0x6C, 0xA1, 0x59, 0x9C, 0x72, 0x8A, 0xF6, 0xF7, 0x72, 0x47, 0xD8, 0xBC, 0x12, 0x25, + 0x69, 0xE6, 0x1A, 0xE4, 0x91, 0xBF, 0xAC, 0xEF, 0xAD, 0x84, 0xEB, 0xC2, 0x3E, 0x68, 0x03, 0x94, + 0x9E, 0xF5, 0x1A, 0xC5, 0x47, 0xA1, 0x80, 0x37, 0xAE, 0x06, 0xBC, 0xD8, 0xAD, 0x57, 0x05, 0x51, + 0x4A, 0xB1, 0x36, 0x84, 0x08, 0x3B, 0x37, 0x51, 0x10, 0x3B, 0xC4, 0xE5, 0xC0, 0x97, 0x51, 0xD8, + 0x14, 0x6F, 0x05, 0xBE, 0x95, 0x3D, 0xDE, 0x3F, 0xCA, 0xBA, 0xD7, 0xF3, 0xA6, 0xD1, 0x6E, 0x3D, + 0x7E, 0x89, 0x16, 0xE2, 0x23, 0x1B, 0x4B, 0xB0, 0xEA, 0x0A, 0x4F, 0xE1, 0xAB, 0x8D, 0x32, 0x9D, + 0x1B, 0x20, 0xED, 0x8D, 0x2A, 0x4F, 0x1A, 0x6A, 0x6B, 0x05, 0x81, 0xDA, 0x27, 0xD5, 0x4B, 0xFC, + 0x65, 0x00, 0xE2, 0xF8, 0x59, 0x1B, 0x3C, 0x0B, 0x50, 0x05, 0x84, 0xD6, 0x1C, 0x07, 0x11, 0xA4, + 0xC0, 0x17, 0x74, 0x5E, 0xC2, 0x4B, 0x12, 0xEF, 0x6E, 0xBC, 0x9C, 0x4E, 0x19, 0x64, 0x62, 0xC5, + 0xCE, 0xE2, 0x08, 0x29, 0x35, 0x2A, 0xC7, 0x88, 0x15, 0xC3, 0xF3, 0xF1, 0x5C, 0xD5, 0x76, 0x9D, + 0x41, 0x30, 0xD7, 0x15, 0x20, 0x44, 0xA0, 0x0A, 0x94, 0x9D, 0xE7, 0x16, 0x2A, 0xA0, 0xFF, 0x87, + 0x60, 0x4B, 0x07, 0xFB, 0xED, 0x5B, 0xCA, 0x38, 0x06, 0x12, 0xF1, 0x92, 0xD7, 0x4B, 0x9C, 0xD1, + 0x1D, 0xE6, 0x68, 0xD5, 0xB5, 0xE8, 0x8C, 0xC3, 0x41, 0x8C, 0x31, 0x89, 0xAD, 0x4C, 0xC9, 0x4E, + 0x49, 0x6C, 0x84, 0xC2, 0x3F, 0x40, 0x10, 0xE0, 0x43, 0xAA, 0xA0, 0x04, 0x11, 0x2C, 0x19, 0x4D, + 0x18, 0x24, 0xCA, 0x3F, 0x21, 0x9D, 0x7E, 0x26, 0x3A, 0x69, 0x45, 0x60, 0x8C, 0xA3, 0xCA, 0xC6, + 0xCA, 0x3E, 0x1A, 0x65, 0x27, 0x15, 0xD0, 0x82, 0xC8, 0x18, 0xFF, 0xE4, 0x15, 0xE8, 0x06, 0x07, + 0xDD, 0x80, 0xB6, 0xEC, 0x34, 0x07, 0x47, 0x3B, 0x54, 0xB5, 0xE5, 0xAF, 0x14, 0x8C, 0x43, 0xA8, + 0x5B, 0x43, 0xF3, 0x44, 0xFF, 0x72, 0x47, 0x69, 0xAE, 0xEC, 0xFB, 0x81, 0xE6, 0xBE, 0x78, 0x91, + 0x7B, 0xDC, 0x13, 0xAE, 0xA4, 0x8C, 0xCD, 0x53, 0xE5, 0x2E, 0x5E, 0x2A, 0x78, 0x02, 0x4D, 0x46, + 0x12, 0xCA, 0x14, 0xB2, 0x72, 0xC5, 0x8B, 0x62, 0xB0, 0x30, 0x60, 0x59, 0x63, 0x21, 0x8A, 0x3A, + 0x0E, 0x4B, 0x68, 0x5C, 0x14, 0xAF, 0x94, 0x72, 0xE0, 0x01, 0x64, 0x57, 0x5F, 0x9F, 0x9D, 0xBD, + 0x3B, 0x2B, 0xD0, 0xDD, 0x3E, 0x51, 0xC2, 0xC1, 0x4E, 0x6C, 0x9E, 0x29, 0xD9, 0xDA, 0x0C, 0x0E, + 0x6A, 0xA8, 0x0A, 0x76, 0x0E, 0xF0, 0x3C, 0x85, 0xF5, 0x59, 0xD3, 0x2B, 0xE1, 0xE8, 0x46, 0x34, + 0xEA, 0x8D, 0xF1, 0x65, 0x01, 0x26, 0xA7, 0xA5, 0x80, 0xB4, 0x32, 0xE1, 0x1E, 0xD3, 0xC9, 0xCA, + 0xAA, 0x60, 0x8E, 0x43, 0x06, 0xB3, 0xE4, 0x59, 0x35, 0x65, 0x4A, 0x81, 0x47, 0x1D, 0x1C, 0x6C, + 0x65, 0x58, 0x03, 0xAC, 0xAC, 0x52, 0x6E, 0x94, 0x1B, 0xD1, 0xB6, 0x0E, 0xC6, 0x68, 0x83, 0x5D, + 0x00, 0x21, 0x64, 0xD1, 0xC1, 0xE8, 0x14, 0x29, 0xC7, 0xFF, 0x83, 0xAC, 0x61, 0xAF, 0x4D, 0xD5, + 0x06, 0x99, 0x18, 0x80, 0xB3, 0x46, 0xA4, 0xCA, 0x86, 0xAF, 0x7C, 0x12, 0xF0, 0x5E, 0x38, 0xF7, + 0x6A, 0xE1, 0x68, 0xE3, 0x88, 0xA3, 0xF2, 0x52, 0xD5, 0x1E, 0x2E, 0x0E, 0xAC, 0x9E, 0x5F, 0x1A, + 0xC0, 0x83, 0x8A, 0xCF, 0x23, 0x4B, 0x69, 0xD1, 0x67, 0x4C, 0xC7, 0x73, 0x88, 0x87, 0x5C, 0xEC, + 0x43, 0x33, 0x4B, 0x11, 0xC7, 0x33, 0x23, 0xD5, 0x07, 0xE2, 0x73, 0x71, 0xAE, 0xF2, 0x60, 0xE1, + 0x23, 0x0B, 0xB4, 0xAA, 0xC5, 0x0F, 0xFD, 0xC1, 0xBA, 0x86, 0x0A, 0xDE, 0xE4, 0x69, 0xF5, 0x0C, + 0x51, 0xCE, 0x28, 0xE6, 0x69, 0xEB, 0xF2, 0x01, 0x33, 0xFD, 0xC9, 0x35, 0x8D, 0x25, 0x09, 0x17, + 0xEC, 0x6F, 0x47, 0x59, 0xE3, 0xCF, 0xA9, 0x67, 0x10, 0xAD, 0x1F, 0xAC, 0x68, 0x6C, 0xF1, 0xFE, + 0x71, 0xB5, 0x8C, 0x87, 0xCA, 0x17, 0x59, 0x6C, 0xBE, 0xB3, 0x82, 0xF1, 0xB0, 0xF6, 0x3C, 0xD6, + 0x04, 0x6D, 0x61, 0x7F, 0xE0, 0xB4, 0xED, 0x01, 0x30, 0xD9, 0xB9, 0xDB, 0xE7, 0xA9, 0xCF, 0xF3, + 0x35, 0xBE, 0x32, 0x33, 0x3B, 0x6B, 0xBE, 0xB8, 0x7D, 0xA4, 0x8D, 0x28, 0xCA, 0x2D, 0xFF, 0x24, + 0x8B, 0xB2, 0x3D, 0xBD, 0x84, 0xE3, 0x50, 0x98, 0xD0, 0xC7, 0xD5, 0x75, 0xD0, 0x7C, 0xAA, 0x16, + 0xBD, 0x08, 0x15, 0x22, 0xF0, 0xE2, 0x85, 0xEA, 0x56, 0x1F, 0xCB, 0xBD, 0xDF, 0xBE, 0x55, 0xDD, + 0xAE, 0x6A, 0x83, 0x6F, 0xCF, 0x3A, 0xB5, 0xAA, 0xBF, 0x9B, 0x60, 0xA8, 0x23, 0x3C, 0x1E, 0x25, + 0x6B, 0x0A, 0x2E, 0xE5, 0x94, 0xC7, 0x0B, 0x05, 0x22, 0x23, 0xF2, 0xA0, 0xBE, 0xE0, 0xD9, 0x0B, + 0xD7, 0xD4, 0xF9, 0x08, 0x02, 0x38, 0xFA, 0xB1, 0x2A, 0x10, 0xB9, 0x5E, 0x92, 0xEB, 0xA8, 0x31, + 0x3A, 0x90, 0x3E, 0x55, 0xB5, 0x2E, 0x7A, 0x8C, 0xC0, 0x15, 0xB2, 0xEB, 0x5A, 0x46, 0xA4, 0xBB, + 0xE6, 0x51, 0x04, 0x01, 0xCC, 0x04, 0x44, 0x37, 0xC9, 0xD7, 0xE7, 0x18, 0x58, 0x6E, 0x38, 0x40, + 0x88, 0x09, 0x2D, 0x0C, 0x26, 0x69, 0xCB, 0xD5, 0x9A, 0xD9, 0x8E, 0xA6, 0x7D, 0x65, 0xB4, 0xC2, + 0x8D, 0x62, 0x56, 0xB2, 0x5F, 0x2D, 0x8B, 0x13, 0xE6, 0x0F, 0x16, 0x19, 0xE8, 0x84, 0xF2, 0xB3, + 0x20, 0x91, 0xFB, 0x18, 0x3E, 0xE0, 0x99, 0xC3, 0xC2, 0x3E, 0xE4, 0x69, 0xD9, 0x81, 0xD1, 0x8B, + 0xAD, 0xD1, 0x78, 0xF6, 0x97, 0x90, 0x78, 0xF9, 0xE1, 0xFC, 0xF5, 0x59, 0xB9, 0xB2, 0x81, 0x36, + 0x17, 0x50, 0x88, 0x38, 0xD8, 0xE2, 0x86, 0xFA, 0xE2, 0xFD, 0x4F, 0xE7, 0xE7, 0x1F, 0xDF, 0x9D, + 0xBD, 0xDA, 0x3D, 0x84, 0xE3, 0x90, 0xF3, 0x0F, 0x3F, 0x9F, 0x9E, 0x5C, 0x8C, 0xEE, 0xB0, 0x9A, + 0x1D, 0xEC, 0x70, 0x12, 0xC3, 0xE0, 0xE1, 0x37, 0x2B, 0xC1, 0xD6, 0x9B, 0x95, 0x1F, 0xA0, 0x4D, + 0xE6, 0x8A, 0x94, 0x39, 0x06, 0xD5, 0xCC, 0x31, 0x4F, 0x0D, 0x2B, 0xB1, 0x6B, 0x90, 0xA7, 0x8F, + 0x83, 0xAC, 0xCC, 0x03, 0x6C, 0x0F, 0xCA, 0xA9, 0x63, 0x44, 0xAE, 0x29, 0xC8, 0x52, 0xC7, 0x55, + 0x10, 0xF9, 0xF1, 0x6A, 0x87, 0xED, 0xCE, 0xE6, 0xAF, 0x87, 0x47, 0x2D, 0x79, 0x8C, 0xFC, 0xA8, + 0x25, 0x7F, 0xBD, 0x42, 0xFF, 0x70, 0xF4, 0xFF, 0x02, 0x57, 0x3C, 0x93, 0x85, 0x3F, 0x5A, 0x00, + 0x00 }; +unsigned int PAGE_NOFILES_SIZE = 6737; diff --git a/build-release.py b/build-release.py index c9144d389..fc169ed35 100644 --- a/build-release.py +++ b/build-release.py @@ -11,6 +11,10 @@ environ = dict(os.environ) +def buildEmbeddedPage(): + print('Building embedded web page') + return subprocess.run(["python", "build.py"], cwd="embedded").returncode + def buildEnv(pioEnv, verbose=True, extraArgs=None): cmd = ['platformio','run', '--disable-auto-clean', '-e', pioEnv] if extraArgs: @@ -67,6 +71,10 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): if not os.path.exists(relPath): os.makedirs(relPath) +# We avoid doing this every time, instead checking in a new NoFile.h as necessary +# if buildEmbeddedPage() != 0: +# sys.exit(1) + if buildFs('wifi', verbose=verbose) != 0: sys.exit(1) diff --git a/embedded/build.py b/embedded/build.py new file mode 100644 index 000000000..cc67c6d9c --- /dev/null +++ b/embedded/build.py @@ -0,0 +1,42 @@ +# Creates a NoFile.h include file that contains the embedded +# web page that is used when index.html.gz is missing +# NoFile.h is created from the code in /embedded + +import subprocess, sys + +header = """// Embedded web page to load the index,html.gz for ESP3D-WEBUI. +// Generated from the code in /embedded/ . Do not edit manually. + +// Copyright (c) 2014 Luc Lebosse. All rights reserved. +// License: GPL version 2.1 or (at your option) any later version. + +#pragma once +// clang-format off + +""" + +footer = "" + +def bin2header(data, var_name='var'): + out = [] + out.append('const char {var_name}[] = {{'.format(var_name=var_name)) + l = [ data[i:i+16] for i in range(0, len(data), 16) ] + for i, x in enumerate(l): + line = ', '.join([ '0x{val:02X}'.format(val=c) for c in x ]) + out.append(' {line}{end_comma}'.format(line=line, end_comma=',' if i=5.0.0-security.0" } }, "gulp-smoosher": { @@ -3092,9 +3148,9 @@ } }, "minimist": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", - "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", + "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", "dev": true }, "object-keys": { @@ -3250,12 +3306,12 @@ } }, "gulp-zip": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.0.tgz", - "integrity": "sha512-oR3t8kn+ccHkSyRcBV5kBLPXrhqTh5d6wBAR7r7wqjNQNBhYvOwPedCwlAaGcNl1qSeXNDn6qOk1Qyxvx9Wrow==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.1.0.tgz", + "integrity": "sha512-XZr/y91IliK/SpR74g3TkZejGkGEmK7CSDjSghT1jXshgO+dFvpLIz9w9fpuwkew6i7k4F+G24TubNgq1ISzEw==", "dev": true, "requires": { - "get-stream": "^5.1.0", + "get-stream": "^5.2.0", "plugin-error": "^1.0.1", "through2": "^3.0.1", "vinyl": "^2.1.0", @@ -3263,11 +3319,12 @@ }, "dependencies": { "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } @@ -3300,6 +3357,12 @@ "ansi-regex": "^2.0.0" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-gulplog": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", @@ -3315,6 +3378,23 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } + } + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3383,9 +3463,9 @@ } }, "hosted-git-info": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", - "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-minifier": { @@ -3451,15 +3531,15 @@ } }, "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { @@ -3484,6 +3564,17 @@ "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", "dev": true }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "interpret": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", @@ -3532,6 +3623,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -3541,6 +3641,16 @@ "binary-extensions": "^1.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -3548,9 +3658,9 @@ "dev": true }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, "is-data-descriptor": { @@ -3574,10 +3684,13 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-descriptor": { "version": "0.1.6", @@ -3611,13 +3724,10 @@ "dev": true }, "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", @@ -3643,12 +3753,27 @@ "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", "dev": true }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -3656,9 +3781,9 @@ "dev": true }, "is-path-inside": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.1.tgz", - "integrity": "sha512-CKstxrctq1kUesU6WhtZDbYKzzYBuRH0UYInAVrkc/EYdB9ltbfE0gOoayG9nhohG6447sOOVGhHqsdmBvkbNg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, "is-plain-object": { @@ -3671,12 +3796,13 @@ } }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { - "has": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-relative": { @@ -3688,13 +3814,36 @@ "is-unc-path": "^1.0.0" } }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } } }, "is-unc-path": { @@ -3718,6 +3867,15 @@ "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", "dev": true }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -3743,37 +3901,36 @@ "dev": true }, "istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", + "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", "dev": true, "requires": { - "binaryextensions": "2", - "editions": "^1.3.3", - "textextensions": "2" + "binaryextensions": "^2.2.0", + "textextensions": "^3.2.0" } }, "jsdelivr-cdn-data": { - "version": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", - "from": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/jsdelivr-cdn-data/-/jsdelivr-cdn-data-1.0.4.tgz", + "integrity": "sha512-0C/7mF2lfPCi9Eg5MtGXc0jJ24iHQdg+k9PGPtKPISsEbYX2kFNuw59ynHjmNw6M9AmPN9GZkYVuWRnbcjFhRw==", "dev": true, "requires": { - "semver": "^5.3.0" + "semver": "^5.7.1" } }, "jshint": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz", - "integrity": "sha512-e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==", + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz", + "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==", "dev": true, "requires": { "cli": "~1.0.0", "console-browserify": "1.1.x", "exit": "0.1.x", "htmlparser2": "3.8.x", - "lodash": "~4.17.11", + "lodash": "~4.17.21", "minimatch": "~3.0.2", - "shelljs": "0.3.x", "strip-json-comments": "1.0.x" }, "dependencies": { @@ -3854,9 +4011,9 @@ "dev": true }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "last-run": { @@ -3926,9 +4083,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash._basecopy": { @@ -4228,9 +4385,9 @@ "dev": true }, "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "make-error-cause": { @@ -4455,19 +4612,19 @@ "dev": true }, "merge2": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz", - "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "minimatch": { @@ -4480,9 +4637,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "mixin-deep": { @@ -4669,6 +4826,12 @@ } } }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -4709,15 +4872,14 @@ } }, "object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "es-abstract": "^1.19.1" } }, "object.map": { @@ -4810,9 +4972,9 @@ } }, "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { "aggregate-error": "^3.0.0" @@ -4887,9 +5049,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-root": { @@ -4914,9 +5076,9 @@ "dev": true }, "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -4991,6 +5153,12 @@ "pump": "^2.0.0" } }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "rcfinder": { "version": "0.1.9", "resolved": "https://registry.npmjs.org/rcfinder/-/rcfinder-0.1.9.tgz", @@ -5366,19 +5534,22 @@ "dev": true }, "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } }, "safe-buffer": { "version": "5.1.2", @@ -5439,16 +5610,21 @@ } } }, - "shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", - "dev": true + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, "simple-fmt": { @@ -5713,6 +5889,26 @@ "strip-ansi": "^3.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5796,9 +5992,9 @@ }, "dependencies": { "duplexify": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", - "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", "dev": true, "requires": { "end-of-stream": "^1.4.1", @@ -5808,9 +6004,9 @@ } }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -5819,20 +6015,21 @@ } }, "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } } }, "textextensions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.5.0.tgz", - "integrity": "sha512-1IkVr355eHcomgK7fgj1Xsokturx6L5S2JRT5WcRdA6v5shk9sxWuO/w/VbpQexwkXJMQIa/j1dBi3oo7+HhcA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", + "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", "dev": true }, "through2": { @@ -5969,6 +6166,26 @@ } } }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } + } + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -6193,6 +6410,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", @@ -6228,9 +6458,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, "yargs": { @@ -6255,12 +6485,13 @@ } }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", "dev": true, "requires": { - "camelcase": "^3.0.0" + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" } }, "yazl": { diff --git a/embedded/tool.html.gz b/embedded/tool.html.gz index 1fb223f4fa07a2d2331c0bdb140766f3aab3ada6..3382e1187a6de563b3132be3c4f61fd70bdd6e41 100644 GIT binary patch delta 6451 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 - + From 8257ba87f5f882e1f39a8bb8b339ea816cb39bc0 Mon Sep 17 00:00:00 2001 From: bdring Date: Wed, 2 Feb 2022 16:32:08 -0600 Subject: [PATCH 014/100] WIP --- FluidNC/src/Motors/Dynamixel2.cpp | 43 ++++++++++++++++--------------- FluidNC/src/Motors/Dynamixel2.h | 9 +++++-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index 27f46fbe3..bf2b561a5 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -25,6 +25,9 @@ #include namespace MotorDrivers { + + Uart* MotorDrivers::Dynamixel2::_uart = nullptr; + bool MotorDrivers::Dynamixel2::_uart_started = false; uint8_t MotorDrivers::Dynamixel2::ids[MAX_N_AXIS][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; @@ -35,22 +38,16 @@ namespace MotorDrivers { ids[_axis_index][dual_axis_index()] = _id; // learn all the ids if (!_uart_started) { - if (_uart->baud != 1000000) { - log_info("Warning: The baud rate is " << _uart->baud << ". Dynamixels typically use 1000000 baud."); - } _uart->begin(); - if (_uart->setHalfDuplex()) { log_info("Dynamixel: UART set half duplex failed"); return; } - - log_info("Dynamixel:"); - _uart->config_message("Dynamixel", " Motor "); - + _uart->config_message(" dynamixel2", " "); _uart_started = true; } + read_settings(); config_message(); // print the config @@ -71,27 +68,31 @@ namespace MotorDrivers { startUpdateTask(_timer_ms); } - void Dynamixel2::config_message() { log_info(axisName() << idString() << " Count(" << _dxl_count_min << "," << _dxl_count_max << ")"); } + void Dynamixel2::config_message() { log_info(" " << name() << " id::" << _id << " Count(" << _dxl_count_min << "," << _dxl_count_max << ")"); } bool Dynamixel2::test() { uint16_t len = 3; + log_info(" Test"); + _dxl_tx_message[DXL_MSG_INSTR] = DXL_INSTR_PING; dxl_finish_message(_id, _dxl_tx_message, len); len = dxl_get_response(PING_RSP_LEN); // wait for and get response - if (len == PING_RSP_LEN) { + log_info(" Test Done") + + if (len == PING_RSP_LEN) { uint16_t model_num = _dxl_rx_message[10] << 8 | _dxl_rx_message[9]; if (model_num == 1060) { log_info(" " << name() << " ID " << _id << " Model XL430-W250 F/W Rev " << String(_dxl_rx_message[11], HEX)); } else { log_info(" " << name() << " ID " << _id << " M/N " << model_num << " F/W Rev " << String(_dxl_rx_message[11], HEX)); } - - } else { - log_info(axisName() << idString() << " Ping failed"); + } + else { + log_info(" " << name() << " ID " << _id << " Ping failed"); return false; } @@ -249,32 +250,32 @@ namespace MotorDrivers { uint8_t err = _dxl_rx_message[8]; switch (err) { case 1: - log_info(idString() << " Write fail error"); + log_info(name() << " ID " << _id << " Write fail error"); break; case 2: - log_info(idString() << " Write instruction error"); + log_info(name() << " ID " << _id << " Write instruction error"); break; case 3: - log_info(idString() << " Write access error"); + log_info(name() << " ID " << _id << " Write access error"); break; case 4: - log_info(idString() << " Write data range error"); + log_info(name() << " ID " << _id << " Write data range error"); break; case 5: - log_info(idString() << " Write data length error"); + log_info(name() << " ID " << _id << " Write data length error"); break; case 6: - log_info(idString() << " Write data limit error"); + log_info(name() << " ID " << _id << " Write data limit error"); break; case 7: - log_info(idString() << " Write access error"); + log_info(name() << " ID " << _id << " Write access error"); break; default: break; } } else { // timeout - log_info(idString() << " Timeout"); + log_info(name() << " ID " << _id << " Timeout"); } } diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index e4b898f5a..ee28407c7 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -46,7 +46,8 @@ namespace MotorDrivers { int _axis_index; bool _invert_direction = false; - Uart* _uart = nullptr; + //Uart* _uart = nullptr; + static Uart* _uart; static bool _uart_started; @@ -114,7 +115,11 @@ namespace MotorDrivers { handler.item("count_min", _countMin); handler.item("count_max", _countMax); - handler.section("uart", _uart); + + if (_uart == nullptr) { + handler.section("uart", _uart); + } + //handler.section("uart", _uart); int id = _id; handler.item("id", id); From d6d2646645252611610205c3e8b471851c8ff80d Mon Sep 17 00:00:00 2001 From: bdring Date: Wed, 2 Feb 2022 20:30:18 -0600 Subject: [PATCH 015/100] WIP --- FluidNC/src/Motors/Dynamixel2.cpp | 2 ++ FluidNC/src/Motors/Dynamixel2.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index bf2b561a5..9fd718db1 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -28,6 +28,8 @@ namespace MotorDrivers { Uart* MotorDrivers::Dynamixel2::_uart = nullptr; + uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel + bool MotorDrivers::Dynamixel2::_uart_started = false; uint8_t MotorDrivers::Dynamixel2::ids[MAX_N_AXIS][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index ee28407c7..e5d99373a 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -23,10 +23,9 @@ namespace MotorDrivers { uint8_t _id; char _dxl_tx_message[50]; // outgoing to dynamixel - uint8_t _dxl_rx_message[50]; // received from dynamixel + static uint8_t _dxl_rx_message[50]; // received from dynamixel - bool test(); - uint16_t dxl_get_response(uint16_t length); + bool test(); uint32_t dxl_read_position(); void dxl_read(uint16_t address, uint16_t data_len); void dxl_write(uint16_t address, uint8_t paramCount, ...); @@ -35,6 +34,7 @@ namespace MotorDrivers { void LED_on(bool on); static void dxl_finish_message(uint8_t id, char* msg, uint16_t msg_len); + static uint16_t dxl_get_response(uint16_t length); static uint16_t dxl_update_crc(uint16_t crc_accum, char* data_blk_ptr, uint8_t data_blk_size); void dxl_bulk_goal_position(); From 0de320f8e87a3848847b15e40177966506bc933f Mon Sep 17 00:00:00 2001 From: bdring Date: Thu, 3 Feb 2022 09:13:26 -0600 Subject: [PATCH 016/100] WIP --- FluidNC/src/Motors/Dynamixel2.cpp | 41 +++++++++++++++---------------- FluidNC/src/Motors/Dynamixel2.h | 6 ++--- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index 9fd718db1..9809bcb30 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -28,7 +28,7 @@ namespace MotorDrivers { Uart* MotorDrivers::Dynamixel2::_uart = nullptr; - uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel + uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel bool MotorDrivers::Dynamixel2::_uart_started = false; uint8_t MotorDrivers::Dynamixel2::ids[MAX_N_AXIS][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; @@ -40,6 +40,7 @@ namespace MotorDrivers { ids[_axis_index][dual_axis_index()] = _id; // learn all the ids if (!_uart_started) { + //log_info("Starting UART"); _uart->begin(); if (_uart->setHalfDuplex()) { log_info("Dynamixel: UART set half duplex failed"); @@ -49,7 +50,6 @@ namespace MotorDrivers { _uart_started = true; } - read_settings(); config_message(); // print the config @@ -70,31 +70,27 @@ namespace MotorDrivers { startUpdateTask(_timer_ms); } - void Dynamixel2::config_message() { log_info(" " << name() << " id::" << _id << " Count(" << _dxl_count_min << "," << _dxl_count_max << ")"); } + void Dynamixel2::config_message() { + log_info(" " << name() << " id::" << _id << " Count(" << _dxl_count_min << "," << _dxl_count_max << ")"); + } bool Dynamixel2::test() { uint16_t len = 3; - log_info(" Test"); - _dxl_tx_message[DXL_MSG_INSTR] = DXL_INSTR_PING; dxl_finish_message(_id, _dxl_tx_message, len); - len = dxl_get_response(PING_RSP_LEN); // wait for and get response - log_info(" Test Done") - - if (len == PING_RSP_LEN) { + if (len == PING_RSP_LEN) { uint16_t model_num = _dxl_rx_message[10] << 8 | _dxl_rx_message[9]; if (model_num == 1060) { - log_info(" " << name() << " ID " << _id << " Model XL430-W250 F/W Rev " << String(_dxl_rx_message[11], HEX)); + log_info(" Model XL430-W250 F/W Rev " << String(_dxl_rx_message[11], HEX)); } else { - log_info(" " << name() << " ID " << _id << " M/N " << model_num << " F/W Rev " << String(_dxl_rx_message[11], HEX)); + log_info(" M/N " << model_num << " F/W Rev " << String(_dxl_rx_message[11], HEX)); } - } - else { - log_info(" " << name() << " ID " << _id << " Ping failed"); + } else { + log_info(" Ping failed"); return false; } @@ -221,7 +217,8 @@ namespace MotorDrivers { // wait for and get the servo response uint16_t Dynamixel2::dxl_get_response(uint16_t length) { - length = uart_read_bytes(UART_NUM_2, _dxl_rx_message, length, DXL_RESPONSE_WAIT_TICKS); + length = _uart->readBytes(_dxl_rx_message, length, DXL_RESPONSE_WAIT_TICKS); + //length = uart_read_bytes(UART_NUM_2, _dxl_rx_message, length, DXL_RESPONSE_WAIT_TICKS); return length; } @@ -289,8 +286,8 @@ namespace MotorDrivers { */ void Dynamixel2::dxl_bulk_goal_position() { - char tx_message[100]; // outgoing to dynamixel - float dxl_count_min, dxl_count_max; + uint8_t tx_message[100]; // outgoing to dynamixel + float dxl_count_min, dxl_count_max; uint16_t msg_index = DXL_MSG_INSTR; // index of the byte in the message we are currently filling uint32_t dxl_position; @@ -342,7 +339,7 @@ namespace MotorDrivers { This function will add the header, length bytes and CRC It will then send the message */ - void Dynamixel2::dxl_finish_message(uint8_t id, char* msg, uint16_t msg_len) { + void Dynamixel2::dxl_finish_message(uint8_t id, uint8_t* msg, uint16_t msg_len) { //uint16_t msg_len; uint16_t crc = 0; // header @@ -364,12 +361,14 @@ namespace MotorDrivers { msg[msg_len + 5] = crc & 0xFF; // CRC_L msg[msg_len + 6] = (crc & 0xFF00) >> 8; - uart_flush(UART_NUM_2); - uart_write_bytes(UART_NUM_2, msg, msg_len + 7); + // uart_flush(UART_NUM_2); + // uart_write_bytes(UART_NUM_2, msg, msg_len + 7); + _uart->flush(); + _uart->write(msg, msg_len + 7); } // from http://emanual.robotis.com/docs/en/dxl/crc/ - uint16_t Dynamixel2::dxl_update_crc(uint16_t crc_accum, char* data_blk_ptr, uint8_t data_blk_size) { + uint16_t Dynamixel2::dxl_update_crc(uint16_t crc_accum, uint8_t* data_blk_ptr, uint8_t data_blk_size) { uint16_t i, j; uint16_t crc_table[256] = { 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index e5d99373a..5787144f9 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -22,7 +22,7 @@ namespace MotorDrivers { void set_location(); uint8_t _id; - char _dxl_tx_message[50]; // outgoing to dynamixel + uint8_t _dxl_tx_message[50]; // outgoing to dynamixel static uint8_t _dxl_rx_message[50]; // received from dynamixel bool test(); @@ -33,9 +33,9 @@ namespace MotorDrivers { void set_operating_mode(uint8_t mode); void LED_on(bool on); - static void dxl_finish_message(uint8_t id, char* msg, uint16_t msg_len); + static void dxl_finish_message(uint8_t id, uint8_t* msg, uint16_t msg_len); static uint16_t dxl_get_response(uint16_t length); - static uint16_t dxl_update_crc(uint16_t crc_accum, char* data_blk_ptr, uint8_t data_blk_size); + static uint16_t dxl_update_crc(uint16_t crc_accum, uint8_t* data_blk_ptr, uint8_t data_blk_size); void dxl_bulk_goal_position(); float _homing_position; From d7c2e05ab5c33eefb539b074e21dd1d5a6938065 Mon Sep 17 00:00:00 2001 From: bdring Date: Thu, 3 Feb 2022 11:33:21 -0600 Subject: [PATCH 017/100] WIP --- FluidNC/src/Motors/Dynamixel2.cpp | 23 +++++++---------------- FluidNC/src/Motors/Dynamixel2.h | 21 ++++++++++----------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index 9809bcb30..c27a14740 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -50,8 +50,6 @@ namespace MotorDrivers { _uart_started = true; } - read_settings(); - config_message(); // print the config if (!test()) { // ping the motor @@ -90,21 +88,14 @@ namespace MotorDrivers { log_info(" M/N " << model_num << " F/W Rev " << String(_dxl_rx_message[11], HEX)); } } else { - log_info(" Ping failed"); + log_warn(" Ping failed"); return false; } return true; } - void Dynamixel2::read_settings() { - _dxl_count_min = float(_countMin); - _dxl_count_max = float(_countMax); - - if (_invert_direction) { // normal direction - swap(_dxl_count_min, _dxl_count_min); - } - } + void Dynamixel2::read_settings() {} // sets the PWM to zero. This allows most servos to be manually moved void IRAM_ATTR Dynamixel2::set_disable(bool disable) { @@ -175,7 +166,7 @@ namespace MotorDrivers { uint32_t dxl_position = _dxl_rx_message[9] | (_dxl_rx_message[10] << 8) | (_dxl_rx_message[11] << 16) | (_dxl_rx_message[12] << 24); - read_settings(); + // read_settings(); auto axis = config->_axes->_axis[_axis_index]; @@ -304,6 +295,8 @@ namespace MotorDrivers { float* mpos = get_mpos(); for (size_t axis = X_AXIS; axis < n_axis; axis++) { for (size_t motor_index = 0; motor_index < 2; motor_index++) { + // check to see if this is a dxl motor + current_id = ids[axis][motor_index]; if (current_id != 0) { count++; // keep track of the count for the message length @@ -311,14 +304,12 @@ namespace MotorDrivers { dxl_count_min = float(_countMin); dxl_count_max = float(_countMax); - if (_invert_direction) { // normal direction - swap(dxl_count_min, dxl_count_max); - } - // map the mm range to the servo range float fpos = dxl_position = static_cast( mapConstrain(mpos[axis], limitsMinPosition(axis), limitsMaxPosition(axis), dxl_count_min, dxl_count_max)); + log_debug("dxl:" << current_id << " pos:" << dxl_position); + tx_message[++msg_index] = current_id; // ID of the servo tx_message[++msg_index] = dxl_position & 0xFF; // data tx_message[++msg_index] = (dxl_position & 0xFF00) >> 8; // data diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index 5787144f9..7fa649729 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -21,11 +21,11 @@ namespace MotorDrivers { void set_location(); - uint8_t _id; - uint8_t _dxl_tx_message[50]; // outgoing to dynamixel + uint8_t _id; + uint8_t _dxl_tx_message[50]; // outgoing to dynamixel static uint8_t _dxl_rx_message[50]; // received from dynamixel - bool test(); + bool test(); uint32_t dxl_read_position(); void dxl_read(uint16_t address, uint16_t data_len); void dxl_write(uint16_t address, uint8_t paramCount, ...); @@ -43,8 +43,8 @@ namespace MotorDrivers { float _dxl_count_min; float _dxl_count_max; - int _axis_index; - bool _invert_direction = false; + int _axis_index; + //bool _invert_direction = false; //Uart* _uart = nullptr; static Uart* _uart; @@ -111,7 +111,11 @@ namespace MotorDrivers { } void group(Configuration::HandlerBase& handler) override { - handler.item("invert_direction", _invert_direction); + //handler.item("invert_direction", _invert_direction); + + int id = _id; + handler.item("id", id); + _id = id; handler.item("count_min", _countMin); handler.item("count_max", _countMax); @@ -119,11 +123,6 @@ namespace MotorDrivers { if (_uart == nullptr) { handler.section("uart", _uart); } - //handler.section("uart", _uart); - - int id = _id; - handler.item("id", id); - _id = id; } // Name of the configurable. Must match the name registered in the cpp file. From 4279bd781277dfe37792e31c99a2bb529790be54 Mon Sep 17 00:00:00 2001 From: bdring Date: Thu, 3 Feb 2022 15:16:18 -0600 Subject: [PATCH 018/100] WIP --- FluidNC/src/Motors/Dynamixel2.cpp | 50 +++++++++++++++++++++++++++++-- FluidNC/src/Motors/Dynamixel2.h | 26 +++++++++++----- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index c27a14740..663f2db43 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -25,8 +25,9 @@ #include namespace MotorDrivers { - - Uart* MotorDrivers::Dynamixel2::_uart = nullptr; + Uart* MotorDrivers::Dynamixel2::_uart = nullptr; + uint8_t MotorDrivers::Dynamixel2::_first_id = 0; + uint8_t MotorDrivers::Dynamixel2::_last_id = 0; uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel @@ -50,6 +51,12 @@ namespace MotorDrivers { _uart_started = true; } + // for bulk updating + if (_first_id == 0) { + _first_id = _id; + } + _last_id = _id; + config_message(); // print the config if (!test()) { // ping the motor @@ -123,7 +130,20 @@ namespace MotorDrivers { if (_disabled) { dxl_read_position(); } else { - dxl_bulk_goal_position(); // call the static method that updates all at once + if (_id == _first_id) { + // initialize message + bulk_message_index = 0; + + bulk_message[bulk_message_index] = DXL_SYNC_WRITE; + bulk_message[++bulk_message_index] = DXL_GOAL_POSITION & 0xFF; // low order address + bulk_message[++bulk_message_index] = (DXL_GOAL_POSITION & 0xFF00) >> 8; // high order address + bulk_message[++bulk_message_index] = 4; // low order data length + bulk_message[++bulk_message_index] = 0; // high order data length + } + add_to_bulk_message(); + if (_id == _last_id) { + send_bulk_message(); + } } } @@ -321,6 +341,30 @@ namespace MotorDrivers { dxl_finish_message(DXL_BROADCAST_ID, tx_message, (count * 5) + 7); } + void Dynamixel2::add_to_bulk_message() { + float dxl_count_min, dxl_count_max; + uint32_t dxl_position; + + float* mpos = get_mpos(); + + dxl_count_min = float(_countMin); + dxl_count_max = float(_countMax); + + // map the mm range to the servo range + dxl_position = static_cast( + mapConstrain(mpos[_axis_index], limitsMinPosition(_axis_index), limitsMaxPosition(_axis_index), dxl_count_min, dxl_count_max)); + + log_debug("dxl:" << _id << " pos:" << dxl_position); + + bulk_message[++bulk_message_index] = _id; // ID of the servo + bulk_message[++bulk_message_index] = dxl_position & 0xFF; // data + bulk_message[++bulk_message_index] = (dxl_position & 0xFF00) >> 8; // data + bulk_message[++bulk_message_index] = (dxl_position & 0xFF0000) >> 16; // data + bulk_message[++bulk_message_index] = (dxl_position & 0xFF000000) >> 24; // data + } + + void Dynamixel2::send_bulk_message() { dxl_finish_message(DXL_BROADCAST_ID, bulk_message, bulk_message_index + 2); } + /* Static diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index 7fa649729..7cd297435 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -38,16 +38,23 @@ namespace MotorDrivers { static uint16_t dxl_update_crc(uint16_t crc_accum, uint8_t* data_blk_ptr, uint8_t data_blk_size); void dxl_bulk_goal_position(); + static void init_bulk_message(); + void add_to_bulk_message(); + static void send_bulk_message(); + static uint8_t bulk_message[100]; + static uint8_t bulk_message_index; + float _homing_position; float _dxl_count_min; float _dxl_count_max; int _axis_index; - //bool _invert_direction = false; - //Uart* _uart = nullptr; static Uart* _uart; + bool _my_uart = false; + static uint8_t _first_id; + static uint8_t _last_id; static bool _uart_started; @@ -111,16 +118,21 @@ namespace MotorDrivers { } void group(Configuration::HandlerBase& handler) override { - //handler.item("invert_direction", _invert_direction); - - int id = _id; - handler.item("id", id); - _id = id; + handler.item("id", _id); handler.item("count_min", _countMin); handler.item("count_max", _countMax); if (_uart == nullptr) { + // If _uart is null this must be the parsing phase + handler.section("uart", _uart); + // If we just defined _uart, record that this is the enclosing instance + if (_uart != nullptr) { + _my_uart = true; + } + } else if (_my_uart) { + // _uart is already defined and this is the enclosing instance, so we + // handle the uart section in a non-parsing phase handler.section("uart", _uart); } } From d1640d61ea0c4d57090fb1aa6b00e32eec9c36dd Mon Sep 17 00:00:00 2001 From: bdring Date: Thu, 3 Feb 2022 16:06:32 -0600 Subject: [PATCH 019/100] WIP --- FluidNC/src/Motors/Dynamixel2.cpp | 13 +++++++++---- FluidNC/src/Motors/Dynamixel2.h | 10 +++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index 663f2db43..3b79111ee 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -29,6 +29,9 @@ namespace MotorDrivers { uint8_t MotorDrivers::Dynamixel2::_first_id = 0; uint8_t MotorDrivers::Dynamixel2::_last_id = 0; + uint8_t MotorDrivers::Dynamixel2::bulk_message[100]; + uint8_t MotorDrivers::Dynamixel2::bulk_message_index; + uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel bool MotorDrivers::Dynamixel2::_uart_started = false; @@ -130,9 +133,9 @@ namespace MotorDrivers { if (_disabled) { dxl_read_position(); } else { - if (_id == _first_id) { + if (_id == _last_id) { // from a LIFO List // initialize message - bulk_message_index = 0; + bulk_message_index = DXL_MSG_INSTR; bulk_message[bulk_message_index] = DXL_SYNC_WRITE; bulk_message[++bulk_message_index] = DXL_GOAL_POSITION & 0xFF; // low order address @@ -141,7 +144,7 @@ namespace MotorDrivers { bulk_message[++bulk_message_index] = 0; // high order data length } add_to_bulk_message(); - if (_id == _last_id) { + if (_id == _first_id) { send_bulk_message(); } } @@ -363,7 +366,7 @@ namespace MotorDrivers { bulk_message[++bulk_message_index] = (dxl_position & 0xFF000000) >> 24; // data } - void Dynamixel2::send_bulk_message() { dxl_finish_message(DXL_BROADCAST_ID, bulk_message, bulk_message_index + 2); } + void Dynamixel2::send_bulk_message() { dxl_finish_message(DXL_BROADCAST_ID, bulk_message, bulk_message_index - DXL_MSG_INSTR + 1); } /* Static @@ -396,6 +399,8 @@ namespace MotorDrivers { msg[msg_len + 5] = crc & 0xFF; // CRC_L msg[msg_len + 6] = (crc & 0xFF00) >> 8; + //hex_msg(msg, "DXL Tx: ", msg_len); + // uart_flush(UART_NUM_2); // uart_write_bytes(UART_NUM_2, msg, msg_len + 7); _uart->flush(); diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index 7cd297435..b6f7f2fd7 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -38,9 +38,9 @@ namespace MotorDrivers { static uint16_t dxl_update_crc(uint16_t crc_accum, uint8_t* data_blk_ptr, uint8_t data_blk_size); void dxl_bulk_goal_position(); - static void init_bulk_message(); - void add_to_bulk_message(); - static void send_bulk_message(); + static void init_bulk_message(); + void add_to_bulk_message(); + static void send_bulk_message(); static uint8_t bulk_message[100]; static uint8_t bulk_message_index; @@ -51,8 +51,8 @@ namespace MotorDrivers { int _axis_index; - static Uart* _uart; - bool _my_uart = false; + static Uart* _uart; + bool _my_uart = false; static uint8_t _first_id; static uint8_t _last_id; From 1b513b2e3abe886c65a72e8b481538ee01c84d2f Mon Sep 17 00:00:00 2001 From: bdring Date: Thu, 3 Feb 2022 19:38:22 -0600 Subject: [PATCH 020/100] Cleanup --- FluidNC/src/Motors/Dynamixel2.cpp | 66 +------------------------------ FluidNC/src/Motors/Dynamixel2.h | 11 +----- 2 files changed, 2 insertions(+), 75 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index 3b79111ee..d29a69472 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -35,16 +35,12 @@ namespace MotorDrivers { uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel bool MotorDrivers::Dynamixel2::_uart_started = false; - uint8_t MotorDrivers::Dynamixel2::ids[MAX_N_AXIS][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; void Dynamixel2::init() { _has_errors = false; // Initially assume okay _axis_index = axis_index(); - ids[_axis_index][dual_axis_index()] = _id; // learn all the ids - if (!_uart_started) { - //log_info("Starting UART"); _uart->begin(); if (_uart->setHalfDuplex()) { log_info("Dynamixel: UART set half duplex failed"); @@ -79,7 +75,7 @@ namespace MotorDrivers { } void Dynamixel2::config_message() { - log_info(" " << name() << " id::" << _id << " Count(" << _dxl_count_min << "," << _dxl_count_max << ")"); + log_info(" " << name() << " id::" << _id << " Count(" << _countMin << "," << _countMax << ")"); } bool Dynamixel2::test() { @@ -189,8 +185,6 @@ namespace MotorDrivers { uint32_t dxl_position = _dxl_rx_message[9] | (_dxl_rx_message[10] << 8) | (_dxl_rx_message[11] << 16) | (_dxl_rx_message[12] << 24); - // read_settings(); - auto axis = config->_axes->_axis[_axis_index]; uint32_t pos_min_steps = lround(mpos_to_steps(limitsMinPosition(_axis_index), _axis_index)); @@ -232,7 +226,6 @@ namespace MotorDrivers { // wait for and get the servo response uint16_t Dynamixel2::dxl_get_response(uint16_t length) { length = _uart->readBytes(_dxl_rx_message, length, DXL_RESPONSE_WAIT_TICKS); - //length = uart_read_bytes(UART_NUM_2, _dxl_rx_message, length, DXL_RESPONSE_WAIT_TICKS); return length; } @@ -292,58 +285,6 @@ namespace MotorDrivers { } } - /* - Static - - This will sync all the motors in one command - It looks for IDs in the array of axes - - */ - void Dynamixel2::dxl_bulk_goal_position() { - uint8_t tx_message[100]; // outgoing to dynamixel - float dxl_count_min, dxl_count_max; - - uint16_t msg_index = DXL_MSG_INSTR; // index of the byte in the message we are currently filling - uint32_t dxl_position; - uint8_t count = 0; - uint8_t current_id; - - tx_message[msg_index] = DXL_SYNC_WRITE; - tx_message[++msg_index] = DXL_GOAL_POSITION & 0xFF; // low order address - tx_message[++msg_index] = (DXL_GOAL_POSITION & 0xFF00) >> 8; // high order address - tx_message[++msg_index] = 4; // low order data length - tx_message[++msg_index] = 0; // high order data length - - auto n_axis = config->_axes->_numberAxis; - float* mpos = get_mpos(); - for (size_t axis = X_AXIS; axis < n_axis; axis++) { - for (size_t motor_index = 0; motor_index < 2; motor_index++) { - // check to see if this is a dxl motor - - current_id = ids[axis][motor_index]; - if (current_id != 0) { - count++; // keep track of the count for the message length - - dxl_count_min = float(_countMin); - dxl_count_max = float(_countMax); - - // map the mm range to the servo range - float fpos = dxl_position = static_cast( - mapConstrain(mpos[axis], limitsMinPosition(axis), limitsMaxPosition(axis), dxl_count_min, dxl_count_max)); - - log_debug("dxl:" << current_id << " pos:" << dxl_position); - - tx_message[++msg_index] = current_id; // ID of the servo - tx_message[++msg_index] = dxl_position & 0xFF; // data - tx_message[++msg_index] = (dxl_position & 0xFF00) >> 8; // data - tx_message[++msg_index] = (dxl_position & 0xFF0000) >> 16; // data - tx_message[++msg_index] = (dxl_position & 0xFF000000) >> 24; // data - } - } - } - dxl_finish_message(DXL_BROADCAST_ID, tx_message, (count * 5) + 7); - } - void Dynamixel2::add_to_bulk_message() { float dxl_count_min, dxl_count_max; uint32_t dxl_position; @@ -378,7 +319,6 @@ namespace MotorDrivers { It will then send the message */ void Dynamixel2::dxl_finish_message(uint8_t id, uint8_t* msg, uint16_t msg_len) { - //uint16_t msg_len; uint16_t crc = 0; // header msg[DXL_MSG_HDR1] = char(0xFF); @@ -399,10 +339,6 @@ namespace MotorDrivers { msg[msg_len + 5] = crc & 0xFF; // CRC_L msg[msg_len + 6] = (crc & 0xFF00) >> 8; - //hex_msg(msg, "DXL Tx: ", msg_len); - - // uart_flush(UART_NUM_2); - // uart_write_bytes(UART_NUM_2, msg, msg_len + 7); _uart->flush(); _uart->write(msg, msg_len + 7); } diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index b6f7f2fd7..88a765dd6 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -36,19 +36,14 @@ namespace MotorDrivers { static void dxl_finish_message(uint8_t id, uint8_t* msg, uint16_t msg_len); static uint16_t dxl_get_response(uint16_t length); static uint16_t dxl_update_crc(uint16_t crc_accum, uint8_t* data_blk_ptr, uint8_t data_blk_size); - void dxl_bulk_goal_position(); + // static things for the bulk position command (set all axes at one time) static void init_bulk_message(); void add_to_bulk_message(); static void send_bulk_message(); static uint8_t bulk_message[100]; static uint8_t bulk_message_index; - float _homing_position; - - float _dxl_count_min; - float _dxl_count_max; - int _axis_index; static Uart* _uart; @@ -99,8 +94,6 @@ namespace MotorDrivers { public: Dynamixel2() : _id(255), _disabled(true), _has_errors(true) {} - String idString() { return "Dynamixel Servo ID " + _id; } - // Overrides for inherited methods void init() override; void read_settings() override; @@ -108,8 +101,6 @@ namespace MotorDrivers { void set_disable(bool disable) override; void update() override; - static uint8_t ids[MAX_N_AXIS][2]; - // Configuration handlers: void validate() const override { Assert(_uart != nullptr, "Dynamixel: Missing UART configuration"); From 0750e6586de13132a1bf5e873bc6814f67355c0e Mon Sep 17 00:00:00 2001 From: bdring Date: Sun, 6 Feb 2022 07:57:01 -0600 Subject: [PATCH 021/100] Cleanup --- FluidNC/src/Motors/Dynamixel2.cpp | 10 +++++----- example_configs/TMC2209.yaml | 32 +++++++++++++++--------------- example_configs/test_drive_SD.yaml | 23 --------------------- 3 files changed, 21 insertions(+), 44 deletions(-) diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index d29a69472..4c37fe8a8 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -34,7 +34,7 @@ namespace MotorDrivers { uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel - bool MotorDrivers::Dynamixel2::_uart_started = false; + bool MotorDrivers::Dynamixel2::_uart_started = false; void Dynamixel2::init() { _has_errors = false; // Initially assume okay @@ -74,9 +74,7 @@ namespace MotorDrivers { startUpdateTask(_timer_ms); } - void Dynamixel2::config_message() { - log_info(" " << name() << " id::" << _id << " Count(" << _countMin << "," << _countMax << ")"); - } + void Dynamixel2::config_message() { log_info(" " << name() << " id::" << _id << " Count(" << _countMin << "," << _countMax << ")"); } bool Dynamixel2::test() { uint16_t len = 3; @@ -127,7 +125,9 @@ namespace MotorDrivers { } if (_disabled) { - dxl_read_position(); + // TO DO need to properly time this. Currently it would read too fast. + // Needs delay between reads or bulk read. + //dxl_read_position(); } else { if (_id == _last_id) { // from a LIFO List // initialize message diff --git a/example_configs/TMC2209.yaml b/example_configs/TMC2209.yaml index cfa6f974f..fe83c92c4 100644 --- a/example_configs/TMC2209.yaml +++ b/example_configs/TMC2209.yaml @@ -123,14 +123,14 @@ sdcard: card_detect_pin: NO_PIN control: - safety_door: NO_PIN - reset: NO_PIN - feed_hold: NO_PIN - cycle_start: NO_PIN - macro0: NO_PIN - macro1: NO_PIN - macro2: NO_PIN - macro3: NO_PIN + safety_door_pin: NO_PIN + reset_pin: NO_PIN + feed_hold_pin: NO_PIN + cycle_start_pin: NO_PIN + macro0_pin: NO_PIN + macro1_pin: NO_PIN + macro2_pin: NO_PIN + macro3_pin: NO_PIN probe: pin: NO_PIN @@ -145,18 +145,18 @@ macros: macro3: user_outputs: - analog0: NO_PIN - analog1: NO_PIN - analog2: NO_PIN - analog3: NO_PIN + analog0_pin: NO_PIN + analog1_pin: NO_PIN + analog2_pin: NO_PIN + analog3_pin: NO_PIN analog0_hz: 5000 analog1_hz: 5000 analog2_hz: 5000 analog3_hz: 5000 - digital0: NO_PIN - digital1: NO_PIN - digital2: NO_PIN - digital3: NO_PIN + digital0_pin: NO_PIN + digital1_pin: NO_PIN + digital2_pin: NO_PIN + digital3_pin: NO_PIN start: must_home: false diff --git a/example_configs/test_drive_SD.yaml b/example_configs/test_drive_SD.yaml index 8423c5f57..f08eb4b26 100644 --- a/example_configs/test_drive_SD.yaml +++ b/example_configs/test_drive_SD.yaml @@ -35,15 +35,6 @@ spi: mosi_pin: NO_PIN sck_pin: NO_PIN -control: - safety_door: NO_PIN - reset: NO_PIN - feed_hold: NO_PIN - cycle_start: NO_PIN - macro0: NO_PIN - macro1: NO_PIN - macro2: NO_PIN - macro3: NO_PIN probe: pin: NO_PIN @@ -57,20 +48,6 @@ macros: macro2: macro3: -user_outputs: - analog0: NO_PIN - analog1: NO_PIN - analog2: NO_PIN - analog3: NO_PIN - analog0_hz: 5000 - analog1_hz: 5000 - analog2_hz: 5000 - analog3_hz: 5000 - digital0: NO_PIN - digital1: NO_PIN - digital2: NO_PIN - digital3: NO_PIN - sdcard: card_detect_pin: NO_PIN cs_pin: gpio.5 From 86a937c4be98fe282ab06ca36bcc98d529ad4c14 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sun, 6 Feb 2022 07:22:28 -1000 Subject: [PATCH 022/100] Fixed folder support in WebUI Tablet tab (#274) --- FluidNC/data/index.html.gz | Bin 122659 -> 122692 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/FluidNC/data/index.html.gz b/FluidNC/data/index.html.gz index 3681adaa71440e9cf9d64f44dcb3c5f75a86c5ea..8ce1516cf0cc528d2fff737379f0bdffe15a903b 100644 GIT binary patch delta 103766 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ȤZrn5GHB>-4GXhbOJ+u(Bvj|;=~ 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 Hxp4^qfruYI delta 103584 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

#8|dvjl&GorfB_ioU+Z;d+FFSnzsD>%V* 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 From dbc0387bc1f367663366856adbb71db2a1993d8a Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sun, 6 Feb 2022 07:23:49 -1000 Subject: [PATCH 023/100] Cancel pending stepper disable on wake_up (#261) --- FluidNC/src/Protocol.cpp | 2 -- FluidNC/src/Protocol.h | 1 + FluidNC/src/Stepper.cpp | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FluidNC/src/Protocol.cpp b/FluidNC/src/Protocol.cpp index ce1ac73cd..64e61f92c 100644 --- a/FluidNC/src/Protocol.cpp +++ b/FluidNC/src/Protocol.cpp @@ -502,8 +502,6 @@ static void protocol_do_initiate_cycle() { sys.state = State::Cycle; Stepper::prep_buffer(); // Initialize step segment buffer before beginning cycle. Stepper::wake_up(); - // Make sure the steppers can't be scheduled for a shutdown while this cycle is running. - protocol_cancel_disable_steppers(); } else { // Otherwise, do nothing. Set and resume IDLE state. sys.suspend.value = 0; // Break suspend state. sys.state = State::Idle; diff --git a/FluidNC/src/Protocol.h b/FluidNC/src/Protocol.h index 019487d65..4d1c7cbff 100644 --- a/FluidNC/src/Protocol.h +++ b/FluidNC/src/Protocol.h @@ -36,6 +36,7 @@ void protocol_buffer_synchronize(); // Disables the stepper motors or schedules it to happen void protocol_disable_steppers(); +void protocol_cancel_disable_steppers(); extern volatile bool rtStatusReport; extern volatile bool rtCycleStart; diff --git a/FluidNC/src/Stepper.cpp b/FluidNC/src/Stepper.cpp index 3ddd469e6..a8260819b 100644 --- a/FluidNC/src/Stepper.cpp +++ b/FluidNC/src/Stepper.cpp @@ -302,6 +302,8 @@ void IRAM_ATTR Stepper::pulse_func() { // enabled. Startup init and limits call this function but shouldn't start the cycle. void Stepper::wake_up() { //log_info("st_wake_up"); + // Cancel any pending stepper disable + protocol_cancel_disable_steppers(); // Enable stepper drivers. config->_axes->set_disable(false); From 66c5708860af8ee480b667703000d790184c7589 Mon Sep 17 00:00:00 2001 From: MitchBradley Date: Fri, 28 Jan 2022 04:09:12 -1000 Subject: [PATCH 024/100] Move all axes in helical interpolation, fixes #262 --- FluidNC/src/MotionControl.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index be369dd4f..50847af87 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -137,12 +137,11 @@ void mc_arc(float* target, float rt_axis0 = target[axis_0] - center_axis0; float rt_axis1 = target[axis_1] - center_axis1; - float previous_position[MAX_N_AXIS] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; - - uint16_t n; auto n_axis = config->_axes->_numberAxis; - for (n = 0; n < n_axis; n++) { - previous_position[n] = position[n]; + + float previous_position[n_axis] = { 0.0 }; + for (size_t i = 0; i < n_axis; i++) { + previous_position[i] = position[i]; } // CCW angle between position and target from circle center. Only one atan2() trig computation required. @@ -157,14 +156,12 @@ void mc_arc(float* target, } } - auto mconfig = config; - // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to // (2x) arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit // is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation. // For most uses, this value should not exceed 2000. uint16_t segments = - uint16_t(floor(fabs(0.5 * angular_travel * radius) / sqrt(mconfig->_arcTolerance * (2 * radius - mconfig->_arcTolerance)))); + uint16_t(floor(fabs(0.5 * angular_travel * radius) / sqrt(config->_arcTolerance * (2 * radius - config->_arcTolerance)))); if (segments) { // Multiply inverse feed_rate to compensate for the fact that this movement is approximated // by a number of discrete segments. The inverse feed_rate should be correct for the sum of @@ -174,7 +171,11 @@ void mc_arc(float* target, pl_data->motion.inverseTime = 0; // Force as feed absolute mode over arc segments. } float theta_per_segment = angular_travel / segments; - float linear_per_segment = (target[axis_linear] - position[axis_linear]) / segments; + float linear_per_segment[n_axis]; + linear_per_segment[axis_linear] = (target[axis_linear] - position[axis_linear]) / segments; + for (size_t i = A_AXIS; i < n_axis; i++) { + linear_per_segment[i] = (target[i] - position[i]) / segments; + } /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, and phi is the angle of rotation. Solution approach by Jens Geisler. r_T = [cos(phi) -sin(phi); @@ -229,7 +230,10 @@ void mc_arc(float* target, // Update arc_target location position[axis_0] = center_axis0 + r_axis0; position[axis_1] = center_axis1 + r_axis1; - position[axis_linear] += linear_per_segment; + position[axis_linear] += linear_per_segment[axis_linear]; + for (size_t i = A_AXIS; i < n_axis; i++) { + position[i] += linear_per_segment[i]; + } pl_data->feed_rate = original_feedrate; // This restores the feedrate kinematics may have altered mc_linear(position, pl_data, previous_position); previous_position[axis_0] = position[axis_0]; From e94638cbb2429ed940669c15eb1cb3bcce0489d8 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sun, 6 Feb 2022 08:51:45 -1000 Subject: [PATCH 025/100] Fix #275 - Failed SD open locks SD (#276) --- FluidNC/src/FileStream.cpp | 3 +++ FluidNC/src/WebUI/WebSettings.cpp | 6 +----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/FluidNC/src/FileStream.cpp b/FluidNC/src/FileStream.cpp index e1b7ecb4c..95b65cd7e 100644 --- a/FluidNC/src/FileStream.cpp +++ b/FluidNC/src/FileStream.cpp @@ -91,6 +91,9 @@ FileStream::FileStream(const char* filename, const char* mode, const char* defau } _fd = fopen(_path.c_str(), mode); if (!_fd) { + if (_isSD) { + config->_sdCard->end(); + } throw strcmp(mode, "w") ? Error::FsFailedOpenFile : Error::FsFailedCreateFile; } } diff --git a/FluidNC/src/WebUI/WebSettings.cpp b/FluidNC/src/WebUI/WebSettings.cpp index 45a86df74..f24d33f79 100644 --- a/FluidNC/src/WebUI/WebSettings.cpp +++ b/FluidNC/src/WebUI/WebSettings.cpp @@ -245,11 +245,7 @@ namespace WebUI { try { infile = new InputFile(fs, path.c_str(), auth_level, out); - } catch (Error err) { - report_status_message(err, out); - out << "" << '\n'; - return err; - } + } catch (Error err) { return err; } return Error::Ok; } From 8b6b5d89ea36d9d156e6965f42ec75299bd2ac9c Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sun, 6 Feb 2022 09:25:31 -1000 Subject: [PATCH 026/100] Fixed WebUI hover problem (#277) --- FluidNC/data/index.html.gz | Bin 122692 -> 122699 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/FluidNC/data/index.html.gz b/FluidNC/data/index.html.gz index 8ce1516cf0cc528d2fff737379f0bdffe15a903b..9147782fe9050f8883670d270776d175195feb56 100644 GIT binary patch delta 100918 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

CPm55s**}->YEzM1QmPOE*NvRcNRu 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 CG1*Z7 From bb63625f1261b5fe06647b2357ea357b4868296d Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sun, 6 Feb 2022 11:33:22 -1000 Subject: [PATCH 027/100] Run aborted (#278) * Fixed WebUI hover problem * Fix #264 - Job killed while opening WebUI --- FluidNC/src/FileStream.h | 2 +- FluidNC/src/SDCard.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/FluidNC/src/FileStream.h b/FluidNC/src/FileStream.h index 11a5e3c92..952d82f96 100644 --- a/FluidNC/src/FileStream.h +++ b/FluidNC/src/FileStream.h @@ -17,7 +17,7 @@ extern "C" { } class FileStream : public Channel { - bool _isSD; + bool _isSD = false; FILE* _fd; String _path; diff --git a/FluidNC/src/SDCard.cpp b/FluidNC/src/SDCard.cpp index be62c6dee..a4516910b 100644 --- a/FluidNC/src/SDCard.cpp +++ b/FluidNC/src/SDCard.cpp @@ -64,6 +64,9 @@ SDCard::State SDCard::test_or_open(bool refresh) { } SDCard::State SDCard::begin(SDCard::State newState) { + if (newState != SDCard::State::Idle && _state > SDCard::State::Idle) { + return _state; + } SDCard::State oldState = test_or_open(true); if (oldState == SDCard::State::Idle) { _state = newState; From 03a89ccb8a49b83904424460151f913bf19e087c Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 7 Feb 2022 14:32:26 -1000 Subject: [PATCH 028/100] Yet another thing that needs to be in IRAM (#283) --- FluidNC/src/Machine/LimitPin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FluidNC/src/Machine/LimitPin.cpp b/FluidNC/src/Machine/LimitPin.cpp index a06e2d739..804b65baa 100644 --- a/FluidNC/src/Machine/LimitPin.cpp +++ b/FluidNC/src/Machine/LimitPin.cpp @@ -65,7 +65,7 @@ namespace Machine { } } - void LimitPin::read() { + void IRAM_ATTR LimitPin::read() { _value = _pin.read(); if (_value) { if (_posLimits != nullptr) { From 7e4436ce9691f51ac93716a9940f72874c971f61 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 7 Feb 2022 14:37:29 -1000 Subject: [PATCH 029/100] Fixed timing-dependent hang with M5 (#282) (#284) --- FluidNC/src/Protocol.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FluidNC/src/Protocol.cpp b/FluidNC/src/Protocol.cpp index 64e61f92c..ff436c438 100644 --- a/FluidNC/src/Protocol.cpp +++ b/FluidNC/src/Protocol.cpp @@ -235,9 +235,9 @@ void protocol_main_loop() { // Block until all buffered steps are executed or in a cycle state. Works with feed hold // during a synchronize call, if it should happen. Also, waits for clean cycle end. void protocol_buffer_synchronize() { - // If system is queued, ensure cycle resumes if the auto start flag is present. - protocol_auto_cycle_start(); do { + // Restart motion if there are blocks in the planner queue + protocol_auto_cycle_start(); pollChannels(); protocol_execute_realtime(); // Check and execute run-time commands if (sys.abort) { From 41136e596d8dabbea0c4bbde2f8e2892d1d18fe1 Mon Sep 17 00:00:00 2001 From: bdring Date: Mon, 7 Feb 2022 10:09:29 -0600 Subject: [PATCH 030/100] Update Homing.cpp --- FluidNC/src/Machine/Homing.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/FluidNC/src/Machine/Homing.cpp b/FluidNC/src/Machine/Homing.cpp index 3cf5d5248..2b66df29a 100644 --- a/FluidNC/src/Machine/Homing.cpp +++ b/FluidNC/src/Machine/Homing.cpp @@ -285,12 +285,10 @@ namespace Machine { try { if (squaredOneSwitch(motors)) { //log_info("POG Squaring"); - run(motors, true, true); // Approach slowly - run(motors, false, false); // Pulloff - run(motors & MOTOR0, true, false); // Approach slowly - run(motors & MOTOR0, false, false); // Pulloff - run(motors & MOTOR1, true, false); // Approach slowly - run(motors & MOTOR1, false, false); // Pulloff + run(motors, true, true); // Approach slowly + run(motors, false, false); // Pulloff + run(motors, true, false); // Approach slowly + run(motors, false, false); // Pulloff } else if (squaredStressfree(motors)) { //log_info("Stress Free Squaring 0x" << String(motors, 16)); run(motors, true, true); // Approach fast From e8f00ffa58c2ee0d1c7283ad4266630b302ba08b Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 11 Feb 2022 12:23:11 -1000 Subject: [PATCH 031/100] Tablet UI handles 6 axes without jamming up (#290) --- FluidNC/data/index.html.gz | Bin 122699 -> 122731 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/FluidNC/data/index.html.gz b/FluidNC/data/index.html.gz index 9147782fe9050f8883670d270776d175195feb56..1885dc6e31c941f3bfa6953f2156cfef1f9cea1e 100644 GIT binary patch delta 76460 zcmV({K+?a8GoTN~r)ZIAKbw#GQPwJ{Fz$5`MZL*337 z7BGuy%Sxy7sUJrSaQ>5Ct@P{@5~8e^EHNcpQ7UqZ=%9C@pz7p8TO_Fhi+PRleDuOV zj^WsgtyfkYSPf+aSYjWjr?UDAwO+4!jS@q+1|;V}oIvtRfKb4wf2>yuDCY#o!zX1nesy@pV2DUn3GF9f_~MA0)oXN8%eq;+wCG z#5eg!e4``r&G&=E{d^?uBNF$&G7|Umk+`oTalcU_9H!~s2M(+H%pFRjahXtF*E{it zLsZA3vcURpa4rtPe`r~ia5Jky9Qsl0P6tIg#3j|HtX%~2sy-XflQZckxbUpkYpR{A zHyvUstot?Eh}KFdaV>8$i?3e3`1Rl}VE$-;`HdOoHw5#WwcDrI2$hp!VgxAv3eK(5 zD+?JDN4*$|F|NiTFD=~6z)(1m0JNt{iYpe+2F;!&VNm7R3cOZAHs% z1=n35ALc$oFs!lvwz1-DIHg#LwudImaYx&(d`>01Vj|EoD#Nz!R{(sAfnx;fb~ zrnZc!#ukhXe;DhVs+($?Drf6vYi8>iYBjRel%x%Y<*&E>x5{i34&VM;Uw!*82Oiu0 zzdCf~fTQjIE5lX}GT8oGx3T?aIYALkrXB->g&yMO;T>^>h6Cia`2cxM2gvI(2Z&)n z$R|F1dRKg+VH9u7qj*E3cvJuSjAEw@O*Q#W(fR7|f0YA|?G#@fx^lqLPVtptD+d|u z6rIH~t14y={aZCa>X;RW4ZEbDN3M_^`dFf2>+TwVIS7WH@TH3QI12`I;au`D{`Q6} z+}i1Q9`f{uW3&Sv(k1zA83_S=^>anbL0)OC40ysp=;4#fsu&XP3Xo8Oc&>*t=YJ1p z&SIRUe+hG?UT4PeEZ9|KMQC?&;f1k}ujR<;y5*kY{>@%njXg{7%@*F;=8by0kpE6R zWs{+j;*}rmglVjVQ_e*`g+z`4jJ~i0ceyO`xv9RyK8rJ8{pMX#&kI8oU|@pb?Hk)s zydweA3y)w*!J{GWsTv%i#FYp!3wZ#*U+ zzrsWmu<8!cKp3AwuwQn@5I}%XeD67Mrx$L-B-DvJ9@o)S84cSLC*PrmZzRyLL3KWu zyrW>Q2scn7je9+dn ze;x*NbnBg9hOGL;m)-`h{|!qR=Ozo9_66V((c+krG2tMXPH{mW1w#N{*lcjsE#?fz z=j)?*l6aq=U`boAt#+m!uvoONd#=*5(8`(itWnF5OKZc{ExfTie|l~`I)2_dTwh-o zBgY1^2Rt=H{^AqA393gW-WrdDmUeYFfA-wSvo2jfrjD5LxQu{C7OWTrr6}szqDVI` zEHm;=kC2$b5QE0>l2m-fg1!AJn1|Nm-vRR>Ds%QyK_d*#B{ZB!=k?P2wbAFgeD|c_ zOAo>ndU1Hgb1JFOR53HwM6F`nu7PaoVvTjpq&g~H^|Z}Qi{gYu-V#K2HrpEWn} zX`PHJ*~YIsczXuS6MJ%2-=QbHhIqP(ua$S&@9V8Ce|(@t>8&=s{oUNem+T#UA3bWdI|W{MiXRVeb+G!l z-flzbX6xfoduNloyVz#Vqv)|PJ|9ES+xUX3gHMY)P3wKV&HPI@9Rir%n63y^YWMVJF$yj_aE{=!_rN zHa!S!SetB##tvWh;}$L9e^%o?mSirC(Qg7TcTwMI@tSqHRq){U>`APHn@Zhbjt}@o z6W7Jwb3xUqU(qc#Prb5EH?dD@l@-D5tKQni<^XuuT4Ef`j&Rg~Lx%ywcw-#@_O^L< zNHY;cSk`k+SgD1#_(Wk|!jY;^KxAaBsl^2JGv6BzEo9MQvLcIOf6%x-{-n2s2crj7>+bPp3#VYy(EWMdGw+#OOy6(c)|tM1}R=K@dx zMjBX7$@L^(16uxIpqL&Mj=~2eKu3}G7Qvo+m!&{<)F>o4!y*?M!t0NQ-gAZ%>l=-# zx$LO8D}6CIMba?rf9H34AR9tB7!Cq0R?-0_Dxo3_qKn$I&UY**T$*oSiZQlXJy+0e zIvbgnpZpze5szU!o=y4X1djou*2P@L|GL!i)#Z(!TCFYjbW33ooUN_*==-5zCGJ!d ze-DZq2*o+X^_;UT^34*La%J)Hr~_yE4u(}|?vQx1!<|KJf1M({kRoupJA%eK_woF{4Q?!nPSmLZYFzFd#!r!G-E5no(QO zF^0-%^H4h5z~eBtA6N>Jv?D|)7FVai(1J{ma2Z0l>@We2oG`V~84dc;U_LOA&my4E zqwdJrYQ1N3e`amRErOIprS>)t<$yS}xYN`visZB10`92YWKJ^3F%}b?>YHuAHKthP zs%LWxnqi?PbY874nKdp0G9M&$E-(47uDmF9 zO1$=>uX{ukpJdY@1>bMM9mtmB0K_}Pj!)?~mMW>0f3hMa1Est?$r`_*0`|M|{N?a) zH!L&@i?+gotWgja7OO=%|6)O2np%*TtyGYg0{>r5f&Z^jyHgNxrXw6QCVrOV#QQSz z+*#OjksH<)$cNjTTjLfUNX_jwi(=PiQS4egid}15m-%&Gpx(robQr@9BiK1fe6#C0 z`y|)re~xrj?y%T&&_jVxn$RUDuO}e>p+klN#gef?@BQ41VsCjNHM_%@1&fy;BrYLH zq~fG9*!^sX)MQ{Nh4zc3R-*usxL{(U#NwgcqFh9vE0R$n{G_DUf`ONiWR-wpH$BXf zepzVTlCImBknt2N>NgfFWAO=+-Gwum`U%43e*+l15aQ25Tk8oW!vSw?lACDcr&zBmp-Z|KL7Nu*{u#W&Er_FqAk zR%e`nz3eJM&n093!O6&-zKif_*?pILOBu!SEb4A-ocr-;e!33Z`i4IV-H|`uP{RY& ze=oh$xet&4gXsbP);}FXQ}%=WvgkL(g7rqWLjE+G`7Gv}SKpzpIEZd{?e~T<({pya-#>^id^bXjElF&67tys8c%rH1! zf`^zcv}XA+7L9y7>1o=a$eA~Y z$5)Vuu!-sAC%rYBhqFLVDD#)|7r8k!?FWWzeG399WO@l4s9_HhS>;Y?+0b7@f3=so zBznr7=eifJKPI<``{X9ydLH0?&eHsN#^PrGc^@$(oQ|rj7bH)~B^T>sfzT`(N_im7 zwX>1snF}?g^^8BqrQJgccaPG!VGxai%V0VVF3E-Vscmosd3^lj(BKF1*gv1TCI^wv zLs(z~5{fc;|AN^m^c^WiU+gto`~nCoPd#e>ohe;X@&F;A|@+Wn8Wwl$ffXK2zp^IxjcF5%BlvzYIU8; zS9Hmh+x+uLL75Xnb-LuEe>&_#Y)u0S=LUz{r3PLScokrq1i*o#l57EV+MrYzb0f40 z>AvuUVjg=h&I_=~o-ycW7SCcxL3+NU03X-3@%js1;1b+$8ZB}B#qyVRt*{%^6#u2> z*F`OWs@1f1oc5T-y}_WBEbI*rh{Nx89Xg6zTNFQMfwfzqv0HPCe>Eg76nNO2m_^Yc zC^!%R14Z1iVitg>_-K@IiyZ!ZccvVU%6%y?os#=(HhakL4hdxQK{sVd)2E|bcZrGv4-cpB+5V(3ZtU$6) zv^tuJM<-+A(Z;BBe^3X3vLLNvTUR(E@+A-CCiUTW_<K|`#^|13j3EUf7%LJR&>oo-LAvLx5e?c zPvH1j6mI)BSf)mq_oFEY_k%)_Ry~V_U-U&`xA_8N0<6Yx$QUSP-)4OySvZHe{Z=9| z1IH*1v#cMo@DEw|JC}tGf@wB4r7foI!lWw}QH=BDU|#nxc3UOmO7lR^%PN2u1(c#@ z)5_e9jQW_De`BvC1$VQ^aE_gF>a$@P16xZJyJ7J*_#x80qv*lw>TxjZHfJB?&Oi3f zVl9Cm1xm_k0R2q5s*lJi41ga>B*szzsw9mRkR@pShwfIKMhtJpgG!+J$3gYup!&X* zw%L;-o2wsGET*yHppwCk^A9Rs_wElW7Wer!6t?uBfBsU$frioba}Tn|BEmH5z5I!m z+6|Xb!k%5`D`4Hhz?;E7vwrGNYZm^ye*Q6|gJlCdlj^!e2P@mYnf?`!x2*0}_MD@4 zJ!bA=&d9^d_0+Ba)siaL)Hq71TP0BC_yFycVd^75g+Qi}2qZF@_UNTHo0@Wokz@v2 zC6!=je;jZ8XO3fvoY5*OF@&OwGK`0#hU33x%`}%8>zZjAdn7fV@^}HtVZIdaayuq@ zIlE2)gOX!!V%R6MxheUEhA91#lGgcAK7lH(20#V3f#322^LYn-(x^oi_X%!@u>`&c~yz zM%&uP*G=fBLO7Wao+|3XRRz5tYc$_8KNX6b3dKuBr_Eo5Nf#A~hYG0As^e=f?x3PB ze>_wibsDJXZCqeX^%0=S!Y2#}QKOjuF_K5(nWs)MB3Z~2|D%h}7a+k|L^)^=hug`p zn1sBUlaLR55b4OewB@l@zAFa^&Q|+9@T0)K!&G+0E#!7P%`wj5`}Ph!^VlKEz@#Q| z{M(K9n;Iqe{bTOJ|rruzS9~*(akZmiG=zdb3lXuc9$F> zAfJsN?Qxx#7~_*ZeAtI2cd%n>q>c@bc`xs=?>fSe06>5_V{ENOWRLF`>m2kt!Cq&B zbI`p?i}#D*)@l6s(V;R>1Z$xClpPD7x$NLrFsa2wgKdz9U`-(u2mu1Hjq}t-e>kB* z=(^pZg^8dyt<5p@3*)bETIio<6VMIghmPSFpolWZP!MmkpjlS4Wiia+kU`#o0YgvJ z8;KCxjWI$AJwbmAM#vr02p}2JjALTAiQ9nC_w{Xh4@#)U7oYeZ6#7PQ5%QPF#J}(d zGFW;&`NgvCU@10lZ7DX@rP%!Lf0rT%u^no6fm0zTc~N?;D^*86EX}wxf}I~=*j@<2 z*3BT;Vp6&pf;K?VSqMSI;A1 z-#YwYT+J5eVooC<6~dVLEm zWYCcT{s9hWYa87mY$xhPf7&^=`4+N`ObDFjn2(LtvE3N{*`h|VO>7Vy7R1gr>kC;S zfME;!!n7^S*;=41EV_+WEJ`$a$X4e#5f4+g|0n1$m=Oy68INIt#xwLXiLXn@x5@cD zWDbK*-N+xHEQ(|nt*g=?{-<90NU^p*cBkj=d2(5=U-6uVS?`-%f7Jn_KoDkOFz~$L zdiEiTyWv+32XeiH@M!7jSCh;ET;mn`<(MTEX zsToK6XMjBmMRwA@;B3*{MKS(+ie8@4!XvPl9?`;QJ^{84iF5deoQR5v#PDDf*GIQr z(CQgEiJo4e)USb&e*)n(^f_^_tf|KyB848GG9;7$sRN0p@NdOSDm55r*A$9U@EJ-6EQSI{+KKU@)a!)t14f696^^y47BmTh*a$3SU= zs0Rj{rW{kJCx9_SG96Kd^b>jlXvjd5UDWq(ynOMb&`b(1L1-e2jik!l7ppXl*V?v5 zXxz-$?7{fhy+~{&x!;>ao1L=Wlr1h{znO+&=7j?y%m(lDpN0owH}F2Ak?i;9M9sbM ze)BE~N_^6ne{ymFxr^1>+0~!POgUf;y)zH^t%2#jG0BW>wkatMeTA**dD7eMY*0#r zCHE(b87)SD@bTErGFJ*hO0ZaIV@Te*ux2q>OCqvx1?b-N@iy`_K z;YQQPNS^(dPwcE`(nnOlqEMfb^N@7O+b=Z6Gz>-Be;3{1kY?0~Rp73D5=`+ZQtiYG z$AgU@{(92KN$vA@Lf>Y(01CsvB2XLf>u0Ww&)QH!p z&q9Lp+sw*prUKq~nTO6wu|X6KUiz=#ttM;KUi1iHnW3V6H63@_aI>)QLph2z!Utdm zJso%6f1i>9A)9d?2J@Njw?#bx2}SO3h$l7gDeatSK}a9#(rMe7-Hi|O(mUmiD;w)< zq=zukp|DGtbbnm}Wg}gNLe&}tB+5}(o<>ExWr#FAC$k{xKfyiw!X5W%n0-MVDMJ|} zu{;O4l{U&3%ofi&@h4*I@8%f&DOe{QGjg;>e{LlG0X@j~6=Yquh2hI|J~_pA026-- z946;9cXsR&gH$bZ4lv;YBWOpJ)#7T(td&3{x*RbskNO#i+NUlamxG`$KGHso79cE* zq~yF*xgw=P9TpQtAOS%7iQ^?Xm610v^lvciIL%>R1RZ|3>R))k4EXWYQnd8ywS}k% zf5~*_PAOJ^ZV32}cd+L=r?Cg{Q9Hpu7W^{D5~*CL+HWr^qG^=Rkg zGNkkI??5?4RO6hw;C>fv`wRHxS? zs?%kN>h$kURD(Mqs)O&As0P;~s=+cue>M0!5S2;xLuY~CQrSd;t?IFG33)hMEng_s zoFbN%jP)L_BOYT5HxQ3c5&*t$2{}8?Qj0ZPtzfYlaQQzfA)6Kxf6O?XPlxU_7R^U} z7Bkt-!dO<(n{6{+ML$A+A)$5Buw*T5X)V+%>JZGr;MBXtq3G@94};3x1n^T9f3Z~q zyF8d`?R+4il+fiZ3m&XHT(`G~*>sEfVDpj-KsW8S_`LaBA4LE|I3L15%O68}mHZ<} zlq+}qWDgjDBi@;E8kvutGk@&jgSh_Ko1Vv`rOutFROMT7g8Y;*oinLHl{#cnbqY?H zG|K*Jyx`3gXx{Y{XOF(Qo1@i z_K?>c_n{1U02n*}EShp8nFr4O4VH#8LhUcdjg>z+C;kUG?jI#5XL6>T&i(N)IX}kC zP$zqQ9AOg~bd_KR%0qePPD`BaXYO>#bG_gUr|!?g)3JK!-`T;=Ix2mre<#KB5AE%_#s$%8hqa^?XA6(o6QT7oZE%IEU{ROm)( zHr`Dc9b23$0E4JB#@^B8^tG`BtF0fO#r;TqjcXYA$>S62_|O`-f8j7^%#tGR0J?c} zx){8d#}Dyo@Vj#Gfw~H?fBX8#75mCM4Z4Etwr^8JeXrioY z63Z5=B#FXqV>>EwC;V+Jp(hh}I*gVwg;uK+*+R2&H`)valc8ZNxW;JJc}2^8@ubIS zuFo>_bv@YNg%_R%f05Up1VbjjL_%!7A#`fP2KfHNvMtPBtOvUO(?b2x*zG+qOtrhj zGea{(cl_AU^a$;{d~&D&dYu=Cc~ARtis-Ko#jEq|(?gz9E_|bC=EhG8%RL87JSSS* zG3m7(kxF9co*(&0uI>Jj*C+pvOSyVn-l1cL5=^sT$kr*~f8`V5Zp1(YNoF}ZP#XJ6 zq=0S|&>t=I%RM4ssK;f31GD$uvO4fb{nYF|4T5)(HTK_;;|JK|I1s zYJ5ypv!VdSMLa*xJ#7v5|XJg2~KQ(7C*RfLmum{&;8G!GnbydVb}Cz6f%HWu4z%a`oZ)( zfYsGoadywbeiO4Szj3dfJnirzqwh)1{#Pxz0sLtjD0p>ab(fFu*qaUy7`IxW&>n)hI-UEVV_8V$fan$ z1(U;pWU?)`b(pCf9i?<3liWK+QMyY7nw%0i*2aC0Pw2gShS3c~S-a+~$3R@#dge>n ze?#}8A(9-=sC=%5Luukl88L6jVHB1uNGH7L7@#gZ1!9#*R$8W zU0lQW&baR$OqmWqhJN|tNhPk(GZ#P{W|jyqg!uG&|2ls5+xpB6BP7DzauALD z;SiVtf;h3b#4+IQ){y*Wgd-l#=f9ZMaZ1BJ&Q;(usaLLea`M;ZpFm$K?RK0 z8?QN~`4?ma0bA!@ObY6LbODq%_Ab0}eLlmP75t36ZCGuuKCoWSP+K6>e*{3y-Z~NC zIt(JN2r^~Mk~2SpU;=xMcO0WH9U#loLwD(0T}VV`rj0=XBk9{mAGv+meGpLQp(1B|_091)~vW$>Ue=6}ke4Dos; z-PYbm_StU!-$+uGwE#{=IrTZx@ z6w|dC=O3{_p;oEIe?1dMsm$&YI;)0MPV7~jUQd23M)w~LMtj|gXU6?MrWVJ+;>q*Y zLyxq;Sk0~pFsiu|#v^Q1ySGCZX?B%q!=CdCj6;4OjG_pK45n>1C(Q_Kqac{B6B|F= z;4JFTu*YQ;jEvsBh2&nKpu@`>*jckuqGXUCqXB=VQAMsZf2&<#tvseMV4wW6$_V5# zy5XKreWvXu--#fP-AJ2wptcw>-9LkMd}Xl?3iOM+-Z=8O`Y@NuVFjRf=f^=fIdo%p zH(qyVGY?l1?NJ7nHeuX+I52&w#Q}L@jV-IN=l$Wot+@u4c`f8S?XjJ7<<>%Wme!Kq zS)9E;-rBXWf0o-!^-E@4ESBEl?PcM%m80Tslv8DAGN}kQ7RStPK zP&H3E1y*_RWxI=VGf08SH@~PDB5Yp+r5 zR<5t^x(0BZP#KMJAglT0pJJF3g4L`E@-Jl#Mh7V3A%B9vXRqoP4nT1&3<6z@_Q2L8 zpr{<~f7EI=00&j|7E^&)A{v%f%@>Dj8e~z5K?~(}+0a6SxfiZ`EXt}9a5Z{(b%2r; z-s4m>r!Sw_wMWnUzdk!Y`ECE`5&k|t+3z18KRFb? zpYIOnd&~$OMqqb$W{=u)04*m_vt=jRUlanXE{c?}}JYtu(h!+uf7HY?CXzO~BkC+qe7D9epKrz4F9zCrO~J zs z<_gOUKw`6>JhHnWJJy|*UJUyszeYF-{UH?IfK!3#(N`L~r!ckecB#^%;kjq3M(O4% z2cs~U7%TA{bG64loTb4BQ$3Mou6X1Qo*ln3)q@=C*|GI1TajC=cX_h&e?|V_OCcZ) zc~7A{JdU|kQ^3Dz6u^&SG;N_MM>bIiCgCBglZ#;jt2Je&ar&y9D-d@5WY~vV6@E-= zhUvuo2#)}TMJ-{fPX>22)&uFcO7wudd->M+$He`hCvZD=kjQbh#JHYejnfu%83DRs zzfHDR+98hV9EEv_!>dp7e~|8(592-D&D`~ZYFh8rF9TOBWDW+MJ9t|yN4|av z+iZAsLT3AJ7`j)d^RqKAv=a^nh#G_RB}A<>sM0I$R$sJgv|HuZe>+snJz)+xZ}j=I zvV|*iR%>~!RQoiX{Pr&+i%QtI>1!3majaHl8zK-4YzS=cwEQXbKOK0%nYE9oc}S?K zeo8b;I7=^KE8nJkvFFy<9gVNv&cti{YGxg+VGl-bcmT(L#s4FGTdg^bUJrl4LOo(} zJ^CyRMxVu!uMjn_f59dtHK%`ue@^Y14gXZ7g;SrC0uM69t5s|ChniW;e;(phm36y!&wdWPUrvh& zzjp{Tvpx+jVN0$%Fo7y(h+D44DMq+Gu>_SdVJ;%D1TrU%*RBFA_Wr@ilm9$YN0l%V z0K>g|CN2WQyU6k?BLFo}&VBLX*^6XMS>Dw9qxm+?on~=J&7ho&#X)4oVqN^gPS}Hy z1E#WLB5?)Be{*!KV3YNeX&B1nGz&Y~m((Q*$JnPNc_=Uf(rlD)(IJqFCLEr)=S(#G znct@}^>C=Fhr>Vox9c+2Xq79IZz&T(dH zD-J^i4FBdVEw>SRM8XJXY?BPC8-F=9BB=d5r4OsZ}; zSzQp8VOOoLwVK^@8bOHS;b=bbhv=wIMY~oSHd%R@*uFbGDg{G>odeo;kV6=2hJw zy}sH1{@kb3!9!XqhFa207e%4}vc z`OOTbo24sc1ytQe=uLtPkAGGVMwaw|HY&Ezso1rYc7@jT;`%5OVr@m$Q?e#S3p4YkbUW+);RV(^Zmo2)bLd4jwG%4Pbh}(Rj_@c8T+Lh zqN6G_%oFAz;u(hzW1OiEWowGm-~S*dvQH1H_1mUbwpRG6u<&cW@PEg`!f#|@*pBi)S^gmpE z>@T?QW#vkhx++y_VSkU3Bqs^w;Mhu3xmv!;$5K_QT}fl@AwKyZ_zP%_uE-8~(I0g8 zgYK?NcVbUO3d^CnW3|YVv{q{8{Xv^QXwyWSh}F6`IR&-^coj2oa{m%G(^@dcmq8>f zSQZd!0$UlnOG+10k5OGo%hSDcyug``y4~d=CxqqIt7a4NttCi>Pl#@1R zAu;UBwyL$m+>6mm+CF1B-=PP8K^BL9r-K3f`wRYe@QnR? z_z3=*6%bd>$0XziqnT@@B<-bZ3`ffd|ag5SKfyA||$mtr9q(Ugeln6nmY zEeQ?RVW}8NtA8~>5ZJeCSOH6c&;<$Be#5RY*WB0&w;d<7T`qL)Lg*Bf3baHFFHtjF znnhl&pNIZX-x1sdsl0GrSJhfOjfo_LTG>WwB@p;9gTRLkI~P-@B{5a4brQ1DpdZ{S z^auH%4$6YsT^77#mnt|8lfzr8xrA%E1$;yqLGp4bNq_PZ51EM@0#zyT6mMqlGg{pWP?rXV?NXTvn+Xj_xuwh?LvzF zLCb#yTK=x->zzM24c+2>^WUt@4`Ta4Y&Q`GbBOF0%vzGR{-%P|a&qocqUo&)#os{S zTeOUnVt=Lh+dE=eYfDL^%dP0tMpk-R=hXXHzFrj{_u4O~@20`!w7l-7YOtSQtTs~K zDfH}L`CA5CeX`KAk7IWOf7$8R3oPro8%38vIJ8rb8CdR<=SOHxk$%}AGVSye2bS>* zYzCJgSfw94u-xCgc;ZIyZ0-3&&;H$i>~F~4_C1#v`pV!_{7mDspNCHgZ3>nB zd{k<9a$1cH7FzA}@OO4HW5$=?=}|e2b_NAbV_P3&LmgVv*-j6t$zHXn<7qjY*|Dfo z>VMRl)tWRUpi}JfH@es7Y8vsWK7vBi8T5tLkB0Z|X_fIB_V^H;C)w4#bb@}EUYtvJ zAL}h|vT89aZTxKF=Y{WGJ`6teRvT8s0%XHK%|>JO!Tob~2IJ$)-l{x~E1gCS3skLW z;EuhDQ?u$$6^i4dY75`Yj(e*=ZPEW$t$$%}^{9zW!eorL8jbf(vkl!eou*aa-j27o ztvdX^sCOW%0S!TJohQb1UTRdwigi|#5}_uR!kVli=COpB<$pYa4y~O=;{q#>@CW{_ z!;g@Nxj;UDrbmnw`+vxfB7CNfBO^Pd6&OZ=oklH%*3>eED*t)(;`qt4 z-{ewNla$HQ!A&*u`1edRYLe2-*=5p%VoS$QdG2nbmO8~(dRdGvDDnBT1|XK^jEU^+ z-o3mW2?wK}ep7uXo1}dzrVNede(_>UD3$EQ5id`vkclU2xMk?9wR~q2cYhED>Z4zQ9?p7k$A7E=Tsfi@WjR<2QEDvykB9~oNC%$-RZ{ny*;Pf$h7)|9u_L@ z^1S=8u{>hu5xEw{LqoifiREjJn&125?QUGJ? z`{C!$JB|C{y?f#PovppfW~b9?_2{X1*mJfvf#jfa&%XDQy%w(7yLeNA3E1QpVGXzb z-@u<%Y}EueWtb;l{m5gCy-ww?1<=0z!mbMQx>$QV?jagsc^oj;?ltX;9je%N>O~G_ zhzDGeLXwJ0_Pn<|=znTHE1i_vi({ z=g*ZC*yI=*A1T!g-6BJwHE3DCG@WcenB2(eKBtPzgL-%vnvmY8rDuo1n%abegA5NtJHzi^kf^;6bvr(UNJYX%DojFTnkH0F(!+)-mWP?W2W%O&Aew=mh zo+$iW80>oKa`ad$DTpzhUby7zxn@8gy&Od=V^4WgI}R@OHXtFVkv{;ft>Ig>^uaue zgGsh%K%9V)Lh>i$(Ud=pmHDapr1KKEOQyy+=DvI~g^UXl&JSstkMz3^TiPRcFsk4h zWTyN_+?k28X@8P6HA*wU6Ap)DXkJx(TXAb^;Np0$uqm(MM#zng#<38KPnQL+R z^sP?*+vK!}do?y=nJjZoSdbAds)D*A(e#?98Ixy+*YnaiB2UO^op?~Fe6?@ zVGYKDQFd~UIz2ervLV?Ue549 z;U}BZLN{LTyemA3LGsGXP;I4=X(nxPZd4L|Z+~2?Kdbu3-86Iac4uGk*!Bn(AEtL?15$H-;w3@1uoo7}!cB z7jCQz|Lm#~*U&vwjh@AoYLWAqiPM8}aA?ojsDa{o%$YfA+}Lw8zqdim-^TfF@<_#` zD#^SqFlb9E&>A+yz39|j+>xfe#cxJA*)a#v7~n#On(c;BFXSgF`Z%xwfL=s1yE0`H zjDOBKVR}Ivh%($+I_;02-ewa&bhBw^p&^m@%WN`*>NwA4tQrLpi<+@l&7~8$#Ik>e zLRDP&{&eoq;LTUrpFc0UxgtbGSzih% z1US2U_Wd&$409OXTMa_=e~s?3AE0<{dj2K|k=%x#KDfVu^&i0U!vSf8g1!i{U4-6H zv=0mT%RdcNo%wXAop?I;K{@p9k{OB85xNV=oqI>)m!k>#Cq|A%75cr?BqYTCH)vS5V;kJqsIgGf^39{!!4^TYAo4 zu=ZWT{`CaMtu1GbQ{_zj=NzARrP0eWrzxf5q_juH4w8YIK*-i4bVW1#YU6DhY z=*(hI@xPL^QA|i#!lV1I@`NY*;nteMt6k+PkZ=pp1%qKdH?l$gi?9Q%st@=C1i`wuBya>VI{5CTNY^sE&7br|#gL zj7(p>sQuM12HT4bvVOi8FQp#?l_lz zM;GTwz^hfs4j=~manR>QD>h=G2LEQLXw9y!TC6`k{uQd&+}N)2dEx=EIAFrMpFRkB z0VF;a1_ln}I)75Stbvi*?PSt#jwp9H1l)-JtOq8bkRsBgfC|^%D#g6k$buUbNm?Ky z%&uL_eRd{y?z|U6Ww!RupA9P;N6_qe6T6GQ$oCnIYSl{OXol;2aw!D|7ED2~ozLWm z>7*QtP=B#Egi;?CSDm6JGgUY_ z3nq<*X7rLWXiQ8~ej@X;P|=$tdM4w(GjONVAb$p?>cX>D>#H?u^(WX9M*wyZTBop! zO=jpbvwDNg;QJg46 z4u5Uj5BKr}ur5UeQcMg??Xj?AXO)#?pXD9F22m9zs3db;Up+)O%%17W)w1mBo)VZ- z+{Dr)qcQy_S{iq)sY-*(>^gN=*=1`QQjv;q?$(|AenN3=q6}PUe@3qJQ2HZ-YPamR5R7Q}?kYCSv=h8(q_dE)T%- ziMQVMYl@uQMY6}%yK#*~T8!05+zR|vnREOeOos61%dSBcxMz~$yBUeS$}TL!>K7U0 z(S8!V^{(x5%R9e*sa zWxTeg>;>uNF13JSdzL%QiVWC`4C0+iczY`=6)(>+!!uiQ=*hUmvS!?P_$**;Va;Fv zylrU4s{au7%Su`gHVM7dqvvQ8UDXwEI)P^9MDf`Tr%0kiJF6@l-Q7-{JSgB$znPtj zc@iRNE+*xN)ein!%qU_!7w1%m5@R62beya)65CeACSg?{R zDAKF6aN+f4Dd$3rRo3qSsH*Dfz(h7F zbtdi|o%=->pGnlH4&8Y|g`7)Gaoea9mi1e}r_M@Lk4dOW8B;Pf&r%Pc%&s`gbLThI z=GV=iUp9Z*yoPdK`F}!Ug;sj5Hp}dZURnvr2mm4716;Z5=)jewWemf-VjHlyruWy& zp{+27w)pdK6M2Lt%F5glA(8P$v>T(`788QguP4!r-bmqLISN-Hv1?1)Ao%n;K4W$f zWz8>P-VD2>0lXP*SqPU~z%P#&SsXn6*_^CH$$!ord2Cae{~(#7F=krL z3Fq^Q0AWC$zb$!WC>uutOeRvTml-j^=bV5|ShMme4 zyVUg?rVlu;r}#Xo{HdqaxCX-1ZWpc|zi9RFM(UA&KDCX@-(Pz9`?tLO{cA6Of9d6~ zdgd^^AP4r645G<%3krWjy-)vWbnWBVn_08@IPz-N#7D<3Xumatz4{*x&-I66&l-AT z_X>-Q$9__zDH;eud{u`m9h4z=bN^^{?L%@xM3)Q@OF*4+DcJ1e3a7v*Z4cQJ257BCc9z_}!Jv%V8e{mA&nzEp)UxNuQg~8Qe>}h!|nK$&q zH13*hnTw==AZ|>2s@IV@k>|Zrb2gI}z0+XMhL)PpEm;PLVq6c-&iL?KFv27EgB1a? zkszWO_50%a5oUkF7>{5Q%$YFCr*7nh)`T37sxt(QzOq>SIfmp0wDej}f$DGc6hO_s zo&xB2sHXrL4zv`9`obZEaOx>|aUAN?m_vOVa|i}w4#8r~A(%`#O@eJ7Rw~k*AjS=t zm;bLx`2=A>KEYX#PtX?R6U+tq1bKdbOX3#TiVJTnmyCbJY${#iHI**0no5^AO{GhW zD(PR;m)VK#M-7)PG|mPdDDFv9W%w<3OJqm(Y`YuhY_n&|isi%Q&1h1WLoq>LYs7gYC>ZO4S5WN5TbI8>1Hf)( zT@+3zBBp9p6rraL39N2M1P$Z!dT6Ll z-Wf?bT?@-s^6)t$*6}rauu#PlU0*Y3Q<9%>aw&iJ)RhK@-7rdCn2nPBS|!ca zX54@93TrApL>F8;?d8JLwWHyNEqiU}^vXEUTkRJpTE4YoNDn;H;WllvHMSZ{RC9D*3uTDOvy<aAqV!cR0LG?B{H?B|=VTD;uLmllK3Zrks3fRSic#lrVoo z>eig{m-_h90(KNu(-}uwmz=MeR^DEvjK5TJE#pn+smghQS2K>mbO*|xkjs_jRi7-`0FZ>ze+B0c-9oPT*I?qkDJpOfgs{@z zlnRyiajB4f%f;hi!BUb)g2f%^u7(Ym$sJbGSswC~f#+cFnyBH`O8Ox;+Yf(VGZso| z?DBmv!EcHrKbgonwZ158aCW}_-w5_?d+iVes`Y7bnK*zw!V*wHT-v3QAE%#7QwDc@ zO>jsiADu9VSD$oah?sd_qXQf5coHXSVn?H`t71Qg5~$GRZkcN6!BBezyraAV?rA2g zQZ%89ab!l)Y3JjsS*9Rf%A0=*v#Cy}l2#&}$h_=)!dAMn^BVQmO**cLp1NAUHP%sA z>9j_@bdwH+F|zFJOPBR#+q@h2A)CjC*sM*ov8JZ*UW=OWzwBn{MOqKbNT)Qdl;5BG z>aJ&|7Mw@+Fe5Q6N2&CIUEMPfN;i|H`Or3!-XiS;9d@VmWyw=a2V6_SnfX4AfBj`11*M+r?ZjmcD zsIAWS^8~;HcIhq5#!7$A#IA8B_@=)IMU@3Os;(fp-If^ultO^&)QL5jM=?HKI`!g9 z&zo9}$O87t+S!S$6*3K|nb$(fMm-^qPuDl;#FOYbd$uUNpV@mh$SutOCjH|$Z48-Y zDtZKYRJw$zE4I~5J1Ng}qquS7``tMS;QZiDn?N!1h{T@Xt2KWV3(}C8GAls{AhF`9 z_Wu^tuU7Zs^=N*|+>cg5IM=#b0&0`F1e<+jrubXG!9-9%JJuq*X~nI$Vvb^`>0k@c zgpT2B$@|h&bi2~6yw0}rarsu>P%F9s$4)v7aTq;2JL8^JGBmc0B-ThdSwmJ+xAQwHuV??`S9|v~^xSv5BH4P?;5n}{ zS+7Ob>rB=g%1UC(?_twxDymtScnTRWwx=!>dLx?R`Xdh27Xt=8z+O$=F{)p5T)x2& z>j^GRlkkhvu%0#>*7F9Q>DdOg_CQjQ&B=F-=E+eOF}HupfAtz~knma;r-Le2eEm$2 zE8uD-_c*awjOf;nQIHVpS!UIXCVA`_VDf+3Tftr>g@y%-w1y_=@`2_XyXIEu@y$Q})%#_~!LTLPV{)lM3rgjk;kfBS zK`8a-^cRQo(5x&Z>@=8uCbdGtqD3A|U%eKHDq^;Z7S^tZ1hoVIyYC6Y@-&X_%`#nl zEa<`9fhu`mGypnpgLgIw28)FwQwipBM73uC79oElfuXIXFmo{bI6Ct`ctf6SubCQP zl49DKk6pvq7fb+E#Bu0^dnn<0hAwJhR&f27DtF0J_Gp5iVCHEUEKx5PBDR*lY~PTk?Tm*iYGjwY|aTr^EyNsXz=1DYM6 zlWi7Qv)$OTwi}JH({ij(TZ)!PjR8iOpW-oRFx9W32!!|zR2x&@7F%4%Eh>&_NN z@3dH<`X-jgs#ufSY-6mHq<0_eDF~xrp?zFmRon+I{wn|-mB&PGx@pC*!s(!Ds*sIGw zo(3>^kB>KA%%?rOOLrezboa3q?+Q-MK0c%td0a$1tovd!7U3^QG$Rg5gM6nvlinBaMmyC}^h&l_s_q}$zQ5%2GLyUU7 zD`TdKA4+fn9`_49a@1tD<}@IfAsDBgH>*H{HEcbnD3!s&9%_{&PznZ6?bIKJ!K{xr zkm4+79wjh@A>jS=s!tnk#S;90GQp7k^(sn&dQjFSD^w{D>r)+A;K(JK*^bmNlF{Ita*;xLSyez=Z!{K2fk{vD z_Oz(4OzCBMd&DYZR0;J^FEtq0qRRpjO361AP=tv}CEUBkYXSj# zj(?+ur0c6V^Gyn%+k{r&)?&EL-fSQ+0G|pggVUs@WaDdlESswNvzn%63#w24umJ0_GC!g5zAZFU$+ zoz5bZHcTjWr_X=R&QyfDQ}Y-Ao0yeFrjlyJWln?0(;k6bwjKs^0I=EV{OoN2q7cPE zw2n@+a)aRBWedQ(D@1eBbrz7W)7V5j$2y+n_C2EDygcTU!ojAZKEavnG4XCsqt-5+ z)hu1z7$DsxMz-NS;@I#tkn+8oibOy#@sSG~B$ne22HuR=l{rN@=rhRDHhhLm6qtdNPN7PA zoe`oL5GjAqxVhA>Cq%J0-43d0{b=ZiHQ$tllKZZ)3SkV5$9Og5&nbh_H&{b8&_^w8 z&ElHrbvUbGB;;UEF)p)zu30~?|C}blp3Uv-SSo+HF&80I*yww)CQ!s_(Shq$NN&K( zfJI)jvuiYYqM<+{-Y6xR{JB!Xq$gPlzu%}S$s)E`)zGl|L?hF*nyEFeWhBir3(XZ3 zB<&T4kROVNW;Sa~TGu*X+`eIic|9bRnO|I3fBi)^&stykh0%i(I*K!RGFzgRZpp&J zM1FsHZ0C(^F&0_PoEyu$ndiIvZDE<2n~{QZK11`@at)W6?FBA|ac;|-1MijYo6lr- zNz&w-KXl3V{k|*qx{8SpsMh4`ESM?n=^k)4z-FlUe~cg54NPSd^LzI==cxigcXM{` zi(qfhMi}evIJUbss{@4QcoY6_rf1%6a;1N|BszL4W+W{*H&ak3ya&#qJU?rGO~zBs zp5e8KC!$+0yv@VIyy6K24SS-1}5hAriLgfGr1 zxQ&~N6vAWoq>~51xIP)yI~ez<9!=oKG@xhe$h+eGa>V=Rm%TtbQ4n@#0c`@i%I$w8 zCoTM7p3unbpo*8yF5MaH5rfz@rnp0mnVHk;T0He9+n!ayrB29L6Cay|4ZI`ipzG(_eNp%A~j^qVf4U1Qu35mf{O z1u`VJ1d>~5B@oK=rburBak24!{)~U&wWsUIz{dW>BS-$Cd|Q-nQ|mm$3$|(KshXK` z(ub_x!pt&fuz(Q%BSXRid#w`6DYG#c^O}sVk_98aJurCh3Bro{#o#rD405s(;uS6C zYHL%hLGw-ihe(fqlLncZkEN$rVAseX_xISR^6A5X z&73@nQkSrq!*nf2=&KsrO&U^VH27zM6hc#rMqS;7H_esu*4etrIFtG@0!@u>r<={j zTYM?nwU3%D3VN!E^S~;dvF(4};p^&7p(kuReaa_JXDQ#{)-58@($f)*=v8&j#6KWTwQi;I5hFXenoRPaR&{oghVDiY`jMQ=FXs2uTJ(nh> zCwcsPVr84_c&f}gbuSi0Q&u1Qc^oxtmX8mas!u$XHRYXU!rN>6uH4)Vsz8rBYR-!- zj_9?h9lE(YOIA#mVw->DXvct=S)xs>kInI@&CHui(chI3k#8lH?gjGcrfBs^2%0=6tU(hV-3m$Z&KDWh~@Iij{mN9i3*v(F4`EN5KOXZ z@#nzXr3l5XhLzSM%LB%I^5F1L!>H$xuCe^#Z8DaQi#MsSxJ!S<%kENkEpJ_-VXn<+ zOy9)1NrsY&STyuyXq`z+H`w*!Tif*vvn7H(sKEK>9%H{n28Vo&J(`C zwuP^yrLmj#Ou8loOx1D?7BCRPQ84vkBkc$C7`56gnsX&pZjHqqGwcAVg3CO}mS%K4 z_5|0awbdKc^e#emX6b zY21}g$^l5iO6?gC<6-okY67=6VuB})HLheWOZx+ask2B>>Q@A3dMUl2j9M_p3m|gD1I`}DZjXb zim3=#76#@@rR^EgRt#zXH;Gf=U?d>C=ri3ZCczArynw_T#N10r=up|5!z5(Fmu$#{ zTvf*>SxJAhAfDpkn`FzlS0qKi&ROAqJq!&6zd!==QisI`MP*{AeWg)LcG_y!O9Q4Y zF)f~hxM5Z=DZLEWq+GqDhp(R5_&~j&PqR>^?rc(*{*os%rK6UB-^R@$TkBtb_Up8o~iM z9xs2IQCh`l4CQBLQPwj~qs&breJmDgLd~eTU9G0v7v+dAutVP(+mElPT&U`QQzdc2sa z>=BezvrT1wd1z;^VsmCvyi~Mzo%`9nK(c?bUZF7QRIAE^DSEhyN379`EPK+{-0VFl zD=Qo%?N+AhwuA?d5#$Kb$Np5UM3nE$s;wC@% z{K{b_78~95*tKg{)qdA(2m(Id+8GUT1nGD=w3e{##chdUn*koZ7gz@WGI%_DEdLG< zmU=07!^F*GA|EUdYf-83k7J&xk>Gzz-MVJvRzJx2zGV;UNihr&b0_uhCjFCb&XVg4 zXCLL81Y*kHG0`jiBTFnu;Z|f;M=iN_X{1_vH@f#^)Vdk{e5zx2H284s^K88Nv8`~J z|LAPg;bpoS);^MlH=}8#j|T*-T^%RYsDqc|(c#)jc7PBLJPiJS-54ybeX~{cEWigD7z;%Azk~Rc})#<2VjO$~ey{Xsxd0lXcwTlBvD)Vk{ z{2gs^uv`sihy4z;O4YjlR=WO5v&Lr48pHP4SwuDIZ{5Uu6a||GLEbX|!fa*W96tH4 z@BZui?>{;I{$IX-I(YKw_rDSR@c5rUIjRb}$3A@c;FJIQvsl?^2Y>I+zB$z;I{fDU z{QdOj*F`ZtHZPW=%W3^%bt$KRS(u;ihtI$L=EdoE-cbJc!|>nRKhVkOr|If6aoh*e&OqHRjNcUOPd|4KijOldnpMxQOBban``rY6s*)kEJ z{Z9TH$$zipzq7#)!+(qUcvPPLEORj{jt5`)>0l@)QMHoiz#k0XoZ%^ski$SwA*`zW z%)P<;CVSSZsGey&!nB-LZ&gbvFY6f|R!>_;`mvG6iT^r2Sj615P~ld7E?JJB*{`)# z0&kz;jZ9}Y8C|cV!t=%Xuj$ zy)^sCnU0ZOg0HL$i#DlWo**Q>`K-V3_27_6G^VGaNRp?P3Mfj3vi(0TFrUYfdHif} z9Gl0_2giv#>KXaWq_ui7zvPH?aXezE`=h}y)PgACC}hRhX2McEbYYayTL07UXM+=c zp~dlH7(TJ!Nq<{{RIIFnK^JW}gNEI2<>*x*$96A2^_dia>KsAO>(sQ-GECzwfA}6$ z8c|>9RU#}hY z)qb_5_OtccabNATR_*r&xf|P3(I8{BYwW{yRIMXpwQKCdbyTe*W3_AS!*x`xBV)B| z?89|bwN2;D0>_g?lfWJR%pb@(r00$m;b1P_o1Ww0!!0Qmnhx1KbRo++FT>6*YueUA z#*QFMb$_sV0QQ%r3*s68J)P-}thKB8)!D>$2CJHMwwBvz{-b*~UCKZPa1A`0i;OxP z_(!3p;?TcROwR0LHbA0^;_aZlPt0fR|6=r$>_9SC&$wUIdj%bU{vr%RbRXtGYJMt2@KPV zs|$w!ISP(*akZ)(`T44--pHUG-?_M;pb6mc`~b3M$Lqi#!O~_Q4&W@dh5=i=jQ}0E zMZ>R-03Emmq<83_Tuf)};q&)a6p-RB(VuXwXFtw_)4MT6^wjM|&2NYqg5(e;rv_>R zO@H1^C}eX#K{Aok(_XgtX)6od!-s1e*KnP=?y_-D4|+*X%YJREvM|B4sDz_Ff-Iki zIPjDKgKPafe8~OIHgT}_nY&i~v9_(tw%E#D^M2?T;lD8KzB4MbMec%i=p7Dk1C_dZ z-m>Pfi`!p*>Dl)WkGn(hI`iHg%l@rl%YQtb^v;ve{CeLvK_fiE9zOhts|B>}vi;4) zu`)|z@lSoivm??=P-S|d`wl6iVjuQOH6(8Hk6EI@;y z@m<5?o3ptUDK9SPEBpMW^=$XZ<8`Ui{(N!Sl2Yf_OZy1Fi@#k7t5LssUC!rq$$txx z{_L|fWczz>6ApGF@1KwdXnucc!V-*I)-OOBeR> zvvRb&FwYbn&GU=0C@$A}YxnTi8Gk-D2yK?nvcpL}|8~AYNzCQx(hhql*tr;8b}(Zk z-qx8!AD>oR|8ClP^D)|b_6?glo8Psizq}Y6s~WQ1(8=tm)?&0Ok7T9~p?GW&YJ<9w z5J0ExZ>~iuS#MVe+UQ|g716BfW4f)?E2Q;W1daD=l12^D7gUuob>vI#)~g{Ft!?h zs|vH;sTh;Gfvt8!-;%(w4V*5ekKwExpKdZ!{6+Bc!mf{2*!U?@mt4u#29xRNYQ+aw zK`VK9f3k)$F4pN03o=0Eh<`=b#;mKK-=yX3<;;Rcgm3*-{=&=5LIgHeXE} z=R0P1*qmyk@lt8V%7lZ*wf^n9>W9aJx+x@iv5Lr0gcLco9zLvY+w8^mcKgO-nYv*$ zoN_z|)6=b!Hr*!T6+McY)%d02l;Qb9i{bu~oKahlKeo1re7tpoMSmq?G3T2sr}v?I>esq9Zair1j#9HIg-N8>)F)Jbq+cV(s{# zFEz;>pwYKF%g5E@M}LrePY1$24nBSIi7o#rJ`MgK;SL79Kq&Sa(Pg{-p{Wh-?eE2? zM2yI@3+g8NT$H-(TybFU3<)~b^QdoAX_Xv=z$e=V;-!zRqZ{F-^>~YiBuxFD-vox=?zZznw8h#C9 zQr=LW^pI;;5JI-E`Q4n5ogUj{R`D#<6n^^LxO;rddy93b+mXD1Zv8EEiR=3x7JD4HF$SC zcs!Lu(1fub_WK$>7U@8kgk~+rp)B8FzZ2(IzUTZk@PC|d0oLQ2=z_X!``(VWb8pZs zowM}jp>cS;>p<&xm~AWd^Jc5VySN0ZPL4gPCh3)kMF*)j3=&_hd$cV+7us8$ey34< zgNwOxD%|E*$KMUU%8;1lEB@o-p8)@a{3ko|&ELTMweO?B5ATPj$v0=Kc6+c@%D*xH zP55uhe}6Ono40Dx=-IC8v!u_Ge!C=1qV1AQCj$#Z9WIyM6PKHLW*dYO50#T z^k{#~+5nA~KMH-dKg&$~@cxIB_3h_5C`7ZQw14ym-+lG$cuj?%dy3r-kPDW?b!57- zc6jqr5ds^154SFC@8EepfC2SD%c~qUHMJ=`O%IO;|0ZQlr#$9)#6Q*V)qGyc>rN^I zk;i(=)^hH8`4|1=&D^XPZx+(#aO7~T)D2?QD}+ups*Fv1r}05Q$nf6l|FBHY<|VDG zf`3NgaLlh_4BsN4(7gE95g=PyOxML;6cwtjPO*P=Cs?aI*OS&H#nhwlg&d^y`(Xos z?48{Nm`~~~Zcg_DY2@(f!5SfLI@dvo%OJC+m#1q@J%91lvGe7OBmM!6_F}{!KycZ8 zanQloVL!eOH}HCR+vN>-uz!HH;X2sSpq-PofzE#<#)|687vD1V@%alA zPQOv}aLWYubg~KgKYORh*|7G~$J?iy&92u5ln6-ZBIDb?e~$U?Wn20X*VBdx-rs=i zv%h}!_vbzDA!hcxhT0LJc&DdblW4;ct(i*u&d)jp`#SXC5*>K*cNm+$gFS3J_KW~6H&qw)sd6N`A>InvduIK>vFHXu>P>aiUxM>@eR$Yj|a|Q29IxG zeq2w?+Ak^lTQ45y7wX#V7Z5Y_w@cdFCFHIs?tP#oT{q&bUdz~p^y9_2PWNDf3E&iISl6!SyiopjPiE`RG*gd)}(Iau$eMN#~;n$_rEB`Hje?!e z@L{#JaVy_&>(@g}Znx>xWcnJJ2{s2^$5dQ&Ji}R?IDS2AsUJ^19A?u9HD%jA4kORk zb5=+bNBH?#8sWibSJgyhNbBQ0U9GMP=dxTm@|EuP1oFcmZds#d^Snu8&nmxrP*4wBj&%+MA4BHbTo8CZ#^bhKBqaeJsn9R?>Qv1Yi2d4cJ ziB>k>+RelQ+zGIq8s=8H|Te0%&`4`)eBD1uKmA+*p&xZ%Mq@=bi`Pq0(Z5S3$ zA02&ac9^GjndN!jFDt$%mVej9l2JvP3mi_}1K7K}?P`Fv{6yvw8M5MIIF;YzqYjX= z14&e#R>#Hr!!^*m3ccoj3v-uRD=?&g9IC)EKMtoKJ{-;1qefihbn-hlpe~t@XLh=5 zlEEOf59!W^m#5yzW$$7=xqSTia2@dPE`|KZr>C+htz37wdLn3PM}PWtcAmdZFQxz# z8OFwrz~JE4)02Ie$A&PpYs?sFNVCV)&`kL86jL)>pNN*f+uAl^tPVE`(3u{@UU-A> z8xt+_pg*vt6H23D6aVr2uYl$6#X=;e>STE|p9yZpZ>bzK9=a{BkVq!(hy|#+lL-qv z5ATczOq1B>;X|+x4}b7b9iWaTz5h1>TnL>9#IjDuFBtiHjPt&RFCm$WQh%0E*({-x<$*KPB~%l6tj%ShV74>eJ%J zbpCX7o0Crl(|^yL*;Ae&-}`pyb>j3aaW;&MkY`P)%bHS0AWd(_8Tt$g%JNw|VoN5z zBVrtxOa)y|S1UUI*oXpJ=hHTD-AB`-+tBn5K}uMr&KPb-{9QD37Q#*;j;o|wd40un z34Fby&DGP_+KpDy$aW+Tql3O?hC6!43hRE}Ty4F(K!3NAC8Y8A%WDA_xqFIPu{>yU zYYe9pxYyvbs~B4%BiG<8e){udeziPDYz}fwc%J9@g7ItDkG+NGxPkAuesmN&ZWtfM z75~*Y6E{XNkHvB+7Uleuu$&)y{N>5ZMdqWr(|%XAO0%%JM|)OaT!AJ;56wHLa+|`}G9-*`;SwCTZoG2q9?{7}fO+>k z0>V76!9*e>2%2GLDW20%Iu0^<88}fSZKjo;kV>nuJ6HOX?$EGwhkm3R{g)r7)qgdrrX9tB^zv>nJS*o{W1$V%Q{-co zFMQtXpT8>Sj>+JvJ)f%YbF26FDWI1DrN{i z!GCG+JdkEETptMK(0rm>AW}*UOX*r>Bu->{WDYTn_*3CerXS5Wc};2wE4C58dyXPm zPzGo~{#md-Td1_5(yhPQ#zVFu-4l}Qm=i6K>F>&XfA>6>#uI4)4QSgkK=Mi_ExFvUW6;R09@y2GC^ zE^j?PJs+nzR7=O9y@u0!ty&5Vu{|N;n0!AT)K%b!%C!7U0i+$iltEKr+-qh zls3_iACy=HVF+5fxLQrmaFu2vpnHB=CK0xha1rK0fld-SUWFAM<=1W&`j`$Z^2i4g zbKNa{{z$lPh3?&eTVN`sxTq5+75XJ=QL-Z4J6W(u11w=b6pUTRfiFYwFcq=PeBveO zD$*^$&Akdch;)*hE)vs4Tz7HQ+kc6`)O3?xCt)t_$|HBfICrWz#I_g-Dptj4c{br@ zAr$N9zAPxND4Y*rUU7#2HF9_Nnq8w3T)9%JbfusmSD+%*nVBTYojl4Hk?b9rIsODZnQf3@m;p{dGZ+&n zNtaQTf)DVxjRKM&BpIhzWPhAQpIdm2)g35;0-frBA7r1vOzZX&f%1TTfh{AUljzb& z%h_UVz|_w}KC@-^$PRH5$U4P27gj0udQa&}URRh}nSL1<4=Np*Avur4T8);1kjP89 z`uz}mLAVga$u#0n^o3w~3j2BK2RT@Qa1sT+@+mx|bv(coC@;if3x6Uh9fIyWe~~H) z5`usW4P$T;EgecDNnK4&5Y94x5bY?Bs;J2}BkWS4EZQLUSsI10RdHfu7n8#?3UUOj zV{~qDaap|`{0|-E^GFtM3PvS}WMny-jby7&0jey1c_kzf3ZOZO%;TpZzzgjBs2Iv# zFZ)a2`+~EpRRMxX41W(S+)D%$p!-ZtMr=cVa4kdRkqRmnY`VFe=`?cModG@NE zCg_u`YLROJRn`QW6X4@OSYR1Uo?YwcF!T&~2`Y>~!$~}-CVy^;Xxafcc;%X8vUalV zk+)ZjK#^U%wl>kv@V|SW{I-xdJ3tXg;y(&XJ{};>?I9Tqt4WJif<5pA`{4=pAST#} znCQ2KWEZVue;A|)An8Yg!@1+^q<;jAWH$%I`PJtp{G%)!1Bmnc&&}_K5~s7b@jQgT z=O1#5OYaUzdVjYq?vJvZ?Y{DHFyj0VTF&W5*x=H;Kr$H4e}r_B#EvlYWh_#r{MS+f zrBoV0K$m}@G)k#FOKas>nuX91h%lL|@{BBVW_V{Q5N44`wd*>rl#QZIG$`HF3MvY^ zDTSJp3aS)eQUP^YT+FYbG0a84^t~*U!t60`Y4xZ}F@HS28zj^$Ktrbwtr6IQ848+) zvPQqSHVUchNF(}yb~neHX-PC?S~BB>-j%FB!2md}c|ajHLvXZgX&cZSHf%46jr{ffr3zQ4wZ{%Y#&fyLDxYADg<0A>-;3C^e?R!=4;i|kOB_=P`80k z(7LPTg-=Q4*55c3mY0Cf(|Dak`@##d>kZ6-~=fYXf*lxFq?&;Z)g!mCiSUq z5Pxvuph3>G!%PV}2%#{Kw46S&hAqJYsA%f(qm%;5G8}yZBl@cVMPRIw0vw5uwhftS zS|KXHDjn!-!T1PrRusmHpqN$%#ax`4Cwb8C`dMo6Jrz@)A^`e)bR#98%blLjMrtJr zVl!{MDQGVV?cSdJ9VGNTHAK8`m-X(pL4T4t>xTgQe7qNe=xz{1J5b;Y0O4K$f;$1= zSY>7ZIzF49TWv0^4Phv)KsOwhVMb#_i5?eGPVF*G7h#T9LFtE)%-;grZUh?%HabuC z?u_>6s05WSf>>?qFffL&SS6*jiL+yYFQG&8J%$%XG`^-$>6#KWb78@tBy{fDh<}TQ zOVi5&eY*+mE*1a+=EF$k%07(^7*H6{UBd3_;Czu&U_4^K0N3UC@grs#DS{fo0sug} zpJOa|6>2|j<}cvQ04RKNyaWy77dT;R7p9$Or326dCn?#ljNg^LT*jFQx)-$6$`eer z_*hCo*k2=@Jg6BuidhUfN*x(6Hh&oO=EK&Sc_pBXU?6Z5MwsY00(lKIV@&Tj(U~9= z=a-|_CRkKu3!;S>`&IK~KjN?aNpL#iz1uwLr*}+mv8rvFplSJU5n(q^)#QO)IG{TV zUeFU{66|E4Qfl-)Z#$cU2CmhmM7m<@XuM*V_-V|DfpI+nQ0lc2!Yu`jgMZPxhRY1C z6}|$hFjj5@x)I?xAh&M5a_c^wYwTq?ngwqH9~@Mf5PtON<5Rr-Q@Z_AzV*{!h*YuI z(bdTz3E=Dku_RToBsF45TFPtq-DF$66u^zf``FQ7=Ux|L%79}xyjw$Ysu1Y4Is7?&Wrj%sxJ*svnwy8)FdAsi5BAfw99yRdFfPbFBxlE1vo8eFBkuK%0?5_4Zw_ZA=WyT#%e!%&#rI*tel_^1E&}A`# zKV>d5tM4Untpf)it4X9`>Sg5$pO6H5^p#QoR6^0;cyOZ;%E0p9f_mc=?qYDlO>Twq zTVZ@+6=fa8qVqct+yZWU!a7Fk3d>30)$=!>1z$&EXMaJJ=viwP{B5%kQ9prg&Y>G} zD6c{ng*j8KvJ$izM{gdj{5 zZe=lO7|9Haicjr`iK9AZf{xg}`sjy@7;%MogNT0@inX0&hi#JA&>IoLuhqKVUG=)& z?G?Memw(3QSjyH~aqDIbBv}ne14jc!))|Ez1Z!rsVz-8>avK=Jo5+9rkg!`tu^j=H z^NsG*d$gzf?RsqtKK(rhpZ?B+kAKg>$G`L7!{2l8;qN#2!4ToF^gK~@sXEob^(Cf_ zH&Tr?LyGW*s>+3%akr;=I?gKw#>fx)^%$dMOMmO@w$#2xk)7;OJ?0%nc%uhr?Zhgt z8%=W6=$DQDH!=fif;wwejOrcLBWLZBfMKu5=3YTkj{a>#1f zdM@>O49@VQ+bc0C1qQ$=8&W$(l86i&P?Kr=o@j%B1p;i8a8dLbOVvM(f6Cf%qO=dK zLQY-E)OY!f5=u%z#WWxjV_Wj`-cr(l)>pT+v`XpYh9DoryP1cL0$QzcflD>!(tl14 zukia5%ubpl%9UWvyFS;a?=5o5b-9DTL9XD%426WO2kWMz$_$jI@m1cb?J$uM3f#E^ ze;6W-`z%&yWWYgCB&Zuc5CK9WeJEWJOr#djjKQ0b`*YPGpfxC082%Ht(0p|XBXQ3K}}#Lu18cN7&N3SBuDNi zkZ&^S4m%9UOg4!iL-f37c5OdRDpzF_>??_*;Y)c1El5(Ly|5Y5EeXZ>lT*qa2*v48 zid4!??M-T_c)(esZ<%D!zhyyRM0u1Dby07)Ix5P4P{(bzK1V5Io;5fWXMfg0>A~;; zzOfOf{uPkr*57RnWd;1kzU-5|;bjIgx-1A(%5$SAszveK2v@oFtA(#!==r2| zqp6Hb9z0&QP}0`Z9$F5bv<>IR7m1G4kIMveBTE3`-cU=z7a?@mxK9x*w*pvhm;ygl z=bpCK7IxRf?0E8@h0%xVGJjIiF&`>h7K%+clh+N{`gL_vTPxRflRkysqa@t7_fQYIS4P>Vg^7{>d{{ zp>D~p(dxQX>$+C2>l!WeV0bpVD3+r-pxu}-W3W^vHk$5>sp>!I<$nlPJK!&wM~0wt z^t&8JlKnm!P&({#(rDQ29$ygx5XdG07L);^P%>pzBrCxe;PI@(!N-EFP=bDx8!zb`B#`wbzvK?v&~ z11Z5rgv7Z$C_+82M`s0`S={UCxR`@{tR4`XVu*%eO$dTY4lp+k>kSwI;`&PDHkL5S zZCD4dqz+0*9jziC__N|SI8u5H)=|fBJea@SHgI-M&#J2>{C{lVSgj;2vZxPVadeMl zG*}=$aOJBV!ep5VeMWuDGmr!UmbLKC*BmNR%T)52TSjW|BYlBP;-fSa-WK|Iimjg^*pi))_A-2LSyN=CH0&lVik>!zYr#YEh<^jnglT<_Yn99`;NBX1>@+oJ^ zker4jzQ;6OjDIfOi_wq8l2$b;xs%*l)hN1qbqbOMqN>WQnQGIy@4}-J>^q(GdGx}n zt62?vX5&b6CK-v~h72Y!)1(KEO7@g{bvMj?bV!uZ9Gar*W zc?q9Vo8BY=y^qG&DF5u1$uzvq;^?CqC+X8hPBj(+5Q!ni-sH*&sYM~Rx5 zc(8QI1%J@Ml|%N&s!5SyNwcQpx}Fo8h>giss)UaeUNYY%S&?DzF&CCiGjPeuQLST$ zH7xwMkBy!Qm*Du^FfftH_ok=1$s6O-XFxGZa>p?q2UWxmCbou)iRL4NVTT?K*vW!G zxGIrim1QH-0<;jwd?C_lq=}#c!yQFlokGpzMSl~C3xa8o`U^GB!=VDQl5hBQY^%k1 zTGbixJY<^ku%E=JBDf9+N~O7Fd@_9G>J)keV+4$RaOkZTR!q}YQsRX^1|F-@nBcFS zz9J@v>Y1ctKKvWxAJ`?ceX81ckE%A_rK;u5?eW09mPriMyofy{SG8WOZY{c%`|nCU`Ekfo&-5|8u6hf#{M*6uNuKYr} z6&&Q9%-3TCdqKsGiYpC~rQAys)nW@c5uBKy$zq0@qdJWp0n<`dGVtu>DBvJYYZ@LM`$%87RFV}CGn zaTqizVk;-Jol>^dOlBu*>4AY44Gu=*+V*C@>ceDct`ih(LZXGmbMp06giYE%q+Vo- zL%JD6Js$d=TBQQ|w1!Q-L(O(*^Cx=SsNQ^!sqsop4n@tNi1rvTuRTatYgry6o8_^F zvQKT+&6TL``i@ES2v_TCZao@~k$;AroQs-q(KZopL|toWHokcp-Viz_gCpA_Qgmb6 z9yTyoRmG+IYIOGFo6&MSU|vfr;qG&~4zm&(!w6XvajdK7wU6{~O7YjKez0Ped0V-r zoGKh6?1(Tb-7y%_bhS$uTUDKre$0XXTFChLICDM(X%<~Hk&>e^$$LM>K!0*5kUU1S zL1#}BZpD8~`|bAVhJ;&=-h!Z!+|Wy-GNWZ6&lZ$peQroW!i6e-OSk^+ngXsS9*)e8 z6J%|C>6^+FL3g}j+bkgO22l&y-Sc&1`*WQAc91x`Cq;9qS0$jKUN{f!Ib#Sy!*g z%;;-iew_erK#{+ju_+H6#qs*k^QJgKPz_lJ0Y?oAq(qb?bl`ThY0jDF)GdEhb+fjl z-zvEEc;G?U!fSTZ<6%EqZxG3UciJdz_3J$&E&jVWqmo8gb>YhWGWN*S8{&=~vxO!I zNKicj1`xsAp?>-*66jVd8z<_U>e#(ux~-tLgPaQcaHL4QyFS*ti)MH*{IPgzw`Z_L z$wx@gfQ>Bn=oKEDOgu0<<|cn8ha6mxLq%(hZ@13o5E(;KPt>kV=}%NT)I+0o8FprB zW~fgw2NjfF$QmkaYwD>p@U?8mrN)hFP_L4-b#&6$i%>0KQC=};dyr`4Zfy7(?mYov z(m;i)m5ba8f9f!Wn4!>{i`Q+flfe!D zzr4oFuD1|8DcJ!2$Hsq6p?Vy(gI{kMQ@=e{O!)lAKmBCKQQ{;}vt^N!#fwI}5sNCcUJlo=_)ulafPn1PvsZH1gfSd1963bse1pRBx`*Q#NzEKkv*F0SU9 z0H*CfGJ+I**$|!9Wa41loM+sJC(w=QoH-esydimGrlZVgV1Yx&BtB6u*DS>SJb}Fh zCZ~yuXp0zCDQJIGDLK`M{r%oi{++#}`dpQSQm3=m^FKeoRL3~o29eH3z$8IP>pUa7 zAlNyRX>4L0))EPRSS#W7QEUvtX-2^$h;@8^Kf$cdmPIjJP3ANy-!OAo9?`2FVb;Ey zZq&kR9Mbr1qNQ$~KM&Pi-t>W<+AUXZyHf?{E_u}Q$kBhqaqNvvTZ6HuG2vMa&~0)- zq9QQG+pFpZt5#3fI)p8$+~5kA!`7E%cN!;5xXhkPr+}Q9xGLu6l3x>izRPq;bu=s; zm=-J3$F;)UkDA?dCKZw8HKgrd_GNhV2&nvUyQxUv?<@@h587JgYU3@w`93cG*NKZy z<($lf#e;u#DT%h35OMJb{weLlwt}XaZI(tBPUTT3Rmt;gv%Nwym?ZEO@`{a&kO zdnh`R^)iNgzbCn0iR7}tNwciycS6Q+cNjBMzjArCtp*#cuqpFW2b+Nv(%CQ7K|*iZxi@}Cvf#ZV#o}> z?HSmLNK>%lgh_&%MDsI&Kx}K#AA7l+zj4drHKY4Q0*teuj72JlWE?)T#sp?URbokm zmCOA}9N#&2ehk%|v{je$Yc;vawr=5Y6%dzsbt?Ybz`mTGPY8Sh`PK7sg6|smZO*1J zs7!xqCvDcX?b1adsFOeYRK{Loh`h7$wkh?2plcl*3TU&H>8Al+L(UKiB(vw25tuIl zZ$~C0X|ujf@-Ftp7#dap*q_Y^kQhL0Hlrq)kpbR@CEhr#9Qs6;+cI(0!{3&P%R;p! z6Blq3$Gg22=)PvNS&(G>k47>Jrho;=S)hM69s26*6r(AK(YfKMJ>yy`OOm0@;JSS{ z`PpC$Bs@aK8TcHhPR>{nTBf4lI$#!?ezIB?tLeMKeSKAywb3mwxl(scP1x&!5E<8k z9C>-+wv_YAx!&`^o&o9?1f>!T$4HOTI-lm1()x}70?0YCm zrRB%y;^U!U4Fm6J&q_WHF*v_2H3*uEpU%qB#pr*8!ZGzCHGKuRaDci%-P$6v|3Q&f zQt9X+%K?h0(wx>oRm~ZrNMv-0sil7my)e`?p!LDWWkx&4+jajKdb!tAdJZSJ4mt>B zyM!Jane@#K4~T_HtIj3MEze~1j9wO>N>P3r8HDU=%s#$ zG^Qq!O_`4Ay8Q4v;$5!1MmNV5mrwySC^-ro&86?9Hr2l$W)-qm`*A|&NFRTxJ$$D2 z;PvLd$-&R-8F;HhMa4swT2EgFfVdj77{xX)Y2l{ovF_6679R1T@0epLU&vw1j0ZTG zW4+=$@Zq!{Me4H;1Sw5qnn@ex5tS2o?{=kG#C_(r@=R89MS7MqIstcdnuruP4-t2+ zCs=N-m}LZz1|w&lL(haz!xVoa41zNcz5+T6Pp^Mx5O!R<8^`9%O1ugNkI2S6U`Dx} z#F{eQDAd%&*Q*d{k0Mp1n^j(M3tBJ~ijyVKs3U1iPh@S{4S8=fSe#VTZ^gLp7E=lg zDZ~Nq=ZUS)Fvpts8h;VQW=ncoxbw@R*@`^b!kFB}PjsIoxDjm%m2ZCx25e3#88pj3 zQBZ%v_NTtg%@YEgGL6T~OhvbD!fiL~+TK8aH3;TMP?ul9 z8`;~Z0eg-xFH^VGp6Nod5HO}7%QE?vt2ZKv3tq$okSY5ilEj)wgE9~j-RsS+kX-eu zS6A;mtz5kVbwS^ED#Cx@+3eHapiw8fPhsuoMXH*D#m9Y9mX-9a<3_F*R5LI{CN6xL3o3Ok&wR39%<3Z)8`@A9$Na#CkM+k)0fAj=si%LVln(3*b%_)xEnZ+3dgU)#N^4unXpjT zI@*mxO$CyeM_yZiy(t3=Oqo?3I0e;SrKeM1iTyY8Aih|T6U45^8E4=$UPe^HGh1f!X(6()P^YH_7olI=#}ZOk?le)B?j0{L}#163wrRWi>q;m8#;wk&}r~9C|9Q?vT9c zEtr3Je&g^Lv>dRteCgIL)sEVsCb{7RngaHFk913?MD)^jVKv_>Q?|5i=n2k-EA6H= z(8+u`eK(&~I@DZ_pDa=F-$jZe10Md^)nc|kw=Pp}-;G0f%X?fTIsr2wb;YWw^=hSn zLEe!d-HLl#2ClX}>E+SXZn=OelYPMmZdbuYE@2Z}C{ir8Hy4(D-s*Mj#Ad zgr*b`cd^54@S&04Wm5_m8<;Sgl`gYPm2)T<`C3h+Y`_6_$_k2X4QBjsu&4%@st(SS^W{yMcvZUH3OcxB2#|?i} zk&!GIJs&Z74noQUgddW12q$tN`|zA%V0dI?7z*5>;Zro>MoL*OQe%5=MM~cUD1@q4 z@b|;owX#+ZtD8nvCXWlGcCELL^|C>h%m)u&dgR30TF@**E<3&B4Qbtk zDT-8Q58&`Z$ulw@amKTW)Fy`!=rKtgbPBq`wt>W-bh@RU1`1tjF5=Y07licoOTyBR zaJnaD4l>E0o)NgMPaPw*0}p@U#U>2K@InCzHs({vORXk!|EE?3M<}!SLER^tMWD%R zOXTE1>`F>Tr%7D2&-`-txSDuROsJV)YZ*%FY+X1{sSUEOh;}Z@dYUkA$hN2cGWCo` zlyqEGyxN%;21|&6gyDm=CxO(8%WZXd+A1rr^I_O!k!D?;HaHh^8hL+KFyA*6GMbGWyK5+7L57S7$c!5@4HIq{4S5rzrgJj}&CpSYR$A7K%tBXZ z7jipP*BRsnpA;dBhAw|po{9!czCy?65WY_yZaKsu6T)LTF-X+t94Tn>e}EB>0<*qz zsAnpq70jdSm|-|R5}+v70+kRoFeuFf%#J}#mQMm)Eh*h0>jV6IeZ7`Mx?)cOc-_#< zwK2-zPwZ88BZ=Z<;-(Ya(W1V_jyf7UzWx!EL`={rf8Yj;)~SE1#IEGvUiK2{HGG^$ znIJs;jhFq-U{s|D>qQWy)bfBLn4sC?9WPK}hlW>Bwl6bVfkGv#F|i6v0oq)514Sq@ zNk*!Q$$T@Tz6L^MW*M8=P?`u0h1BVodGv<4hKFR>K3IACs(UKNnx6oT|f|3aM+ZI4-g9W#pyjxuDaLU{RaL?h1L zvw<|CN56Uu2v3?)d5+NVn2E$NSJzzKq5h(?0PZn0%bR~=$FyIQ4m?^OGoR)GTHmv9 zLgI0$p|GHu9U;I*FF#d8lMRc!0nID1EO2U}Jo-F@^mzci@@ftZk%2dTbd7yVuo~gs zR2YFk8F8fUKY1E3vx`P1F6Ne2r>tl~PMXz)H@GI_fJaT3TH?2^c*YaYLZLvY8=hPH;dM}NnSehl=HtY7GK$c=U&^4q<&ahx zpRoG7`D~o9oXR?cVPd-1Pv6*Af)jeJ^HG1hM%JE@>gm zXuJUXZt66ygHBaDNUq)V_<;^A>vx4(2gSeymUWQcm2&@lXoo{kej|sVo~tjFCM)Z^ zxqb(yaokkD&F!0u5tyUiID};Oo;`lzg%#>ld{!!T);&G`&sVj z%{#K2CZ5XrN^QOa-$^rQhQsQVc=6s`g0^3IHb#%gdT@zNd? zn*{%~17VtwomFk>gw6UQ5?FuS&xD3J*xZ30Kv$5>-VCrJHT;CE>zi+ZievQr0Ag@8 zt#HN*?z)kJZhT=CvdG3f`|9_lL<@g_pthB(Ne8B>w9>4(0ohN7kpM zsCl&pQ!s=kK`wO~adT6ZXA%noC?wOx#OAtYgT~d*p@SkU8;~NxT86UOKxM0avn*z3 z6Q)+j292DfN#x`K#2%)S2zY-PE)rH_g0Mm{e1Q_sP?j6PMw42rqZ3faT-*8yYE|Qm zi8COfD?-k_#k!Luc7uQPE!uGY^MV}TYkbP!?`O>93_(K%om>_oQg|bY6178|KotXH z6)vmM>c^EP6^3I{vTmf?ycxp(2scCcJFMZx>gam{j7eT;vvAS?PMze(gVVVypb6lN zEu3*1&hQU}&j4VCYe}APBm3)VSrto=fHgwpJ9Qr09ZmAxYO#N+S1*rfW@0a_eI_v1 z%zPD^yB~7BPw%QMH9FlS*z5#z^m!+k7yuuM%H^NFScE!Gq1SUCdCK$LHe{R}q9z+% zlRPclX?*yYu_ung#Q}XtxIud`JA+S<`!d}f!C?XTEh0D!+|EV_hwtGEr_?WXKsp|S zt5!bGaKDDG1>=9Z?}-WbF+gl0)gXHfw!v^bT1{TfO%6yH)cibH_)4jo`y`?ymeb=G zSKg<*`rCN-vrOf7e%K;<^^>XR)~(sch?fQNq{nxk;Z(|mrP_B@KbVhIQ{J{g`5$n5 zJY;;$&gT&x4czo&=B9^_sB+?r2WUMbhSwBnSJdZNeY$_ZxcOR1>$~k|``vBoKr>5k zSPx#y%mHOQC==C2sMl>mmw4BHQijvsf<~b%XYgcZ*%UuPIQoaQY}r>oB5HE3P@3n2 z7f|a5!xLO}7UE?iL{6ec4&QqEm}h-t5&T}z-H{v{`3E)A=H#ZpQ6{C}WdDj3KdzCaT&qA zDfJe)u$}V%SdO@h4wt*c(8L;=j2PfSrEx^7KtvYEvlpP!hN{9~Xen1@E*DOgdSz{t z+0_YWR}3I!fCU*%T$AG&SIyYgP(M@HS|RF3mc)N_g&R8tNQJS<4r443ypXFH7cNp- zdSTl1%UA%xe+!`(^`}Ug`-*-WR`q54DC?VAJ!OWeZwOQgkEEX=@2|kskJhoxY*g!yWcLQ1&N=IR111bIcc-S>a`n;WASw^ z0uz71glBprs7sKqZpZkqqE`^34m9wIo=@1ttQzR3RuHMhCO4_5G?F}mJelYOG4UE4 zuTqnqJg2TU7daN$`UTcZ{Y+CD1^gr|V*y3}TgbtJ8xnoErGnz%wj-1l$BRsFYzuVa zGOlYsFi!GeWr!@MSFrJzr#)07O^h&5O7DNPStU+uZW6OTQX1(rax1b-6YW67Xj!?% z#bxzYZxh08L-|5Rl0f{^n-LLzN4I9ZKGzukZ6=;1;suC|x~&3p{RyvAz<|u1Fg2`t zmis1EYhd}Ezk(RT?RAkrsxrP|2%W!CKbioBM5d=i2?Q>K^ljui#V$Fh{i=c zr$PgNa5qG3G!|N1wiL9$n~kB_NYo!swJ|1#fw>+hs8g7!bB7#Vz)%t!olX+cl`w^l zkkLQJH55xHiYj%sHpmmBO{v;mI2Z)WpHbjt;j>rFgSlQKL7_^ z2Ds59%^7!L!Y*1_-ZvTW##?wbUm~wnWZYW2zRkXo9csU3>VkLpHJk62jaDX_9@E#k zky#-Wy?dU2u;+DduWo3x(zkJr#(3RVZ{1OOIT^hws)4;fC~dOvT1Tk*DK-M48{7NP ze@k3to^P@d2gaE>KzT49odBkLtL6dz)SW)w&p}YTX!B zr=h>eRZ_QfU*aX#TSGqLogp7|@V?8^G&8q*|g&EWNY<;<;E9@Mf}Y)snF=fXoUqmIFcRVEA; z0b_WLO*cgh6L=6!9W-Ia4wt^|ystG^GZQ@Qkb^mv$q+uC(fEI%dMd!vI8H0QjuafJ z$jG$P$iEXN)WfvLqxY)hMJJ=)K=TsNts+fTJegaJDUb7Wthd+)rYSzPzq_`UxvYO4xl*V|LQVkL0At<{n5qto z5B3WmV*P>-=8wLGKs(}PhtW;*&V>*&qS!=0+8j1|Nm&TtJ_?c+3l!GNC@{+?OmPAv z>X)V&)Vb2R%zSPFVZ7%S_3$RQ_E7907aZ(r&mmW2nBR#?K9=Q2NH$oa%$%(u4$dfy zZ8UJM_lJc2f3mDw8uvoki^r>9HLS{s;(hiA2b5g``_ z_h!|7rN+O+kA73c3|^P>%geX!c)I#=V6RTo$oVY3(bYucPZt)%m+Y|_zgmTxn}VzqWZ8TJJ!pU20l#F%LF9i_3v!0gfrUZpl+Fbiox4Dor3zQL z;%dv^dMpCDP*M`^&qo_E&}A(jt@Cjb(gt}d2hX;IvhMX)D3tHHHRE7HQXW_d`k zwBki`Wu#-26)v?Eyuc+PL->1aY|wbeNo;=<-mL^2o;7-}DZJa~u9*y{K6d(I?i$eb z@ZF*#kFAb8*rp>tTNdMKh0H$Pt9$oe&KTD@W_B_>zR3u4qg=(le}iPjzSR^tb$S@9 zNWx6Rt#3J+tzOfuOKlIB3MLW~s@NdO9xVHpdW&G=-9n>K~gZ&}0s*^%uSA1j)KBT&=m5TF_|a z@J(M-`dtYw#~@Sn$-AZslj_dcDl~sP4x8pwXep>`?PXZoGOS`Vt(^e%qF7rp)~dqK zrma%McG%MHpOy0~jglxk`t@&Bg)_LBAnz1ge^KiXZa7m}d#0i~r>0JpMqE&MO9Z7+lA36u zAls1#e=W}IR+jAbITHYBv`1xB&(QRpjWrFpr=Eih1E#Nf zY;OL=xlDgK=4owSE;MGFsh1S*8p;!>3riF>KlE9PvDCu2!_(VPZw`OfZqQ5X4Kh1H z7tqwKS}nGFYQrFBPf8IAi9#Kwlg2gH3}R;Zkf-iH^a!b}X9(XiEg`%djKi64f+I>e z`{WA@ma?8Egh9ra05?OypGAbVM1(fxgp(%+d31axJhpCIp@{>5mOf-O6G5M(|}E`}JkinFb9(){Zkxfh!oGKO;>ROi_RAFi!^YmDX-A=7LZt znBs-{?~s9@54tk!(E{@!Qw~)i^5$?VI1`)*org$co=mWeMl?lm?Q?PyC?%M+9ETrU z^9!?M1bSZ@!bx(vt0tOQl^$RT-zw~(4R%y_z|h9Zwcrmo;deLy>7KhTf|1k(&BOo_ zW-bD{Bj3LArQW*nS9FZ&C*HgXvCN&Hnxr8voNuL=WH;gP3C-1M*cx%GZ zI8`HpK~UbZovQdkP9h1^7fCZvsaH~f*K2HD9O$(SdAwsIm9M6^AOL%l@7tDh9()k< z`iC9|CK7^vFEH=&x)|TfZ#V1=H{OtOk^$DKC9mzP(u{v!cD+o88}Ax!98WL@vZ0vH zGRisoj-vQlCe&m?D~s!*G-2B!s04=1x6s)uz#GSya065;YEb(P|2$*@4=U@Id1PZV zL$6A8)z-`>Q#u+SoDhmlRhDWxvs4dwR+hR!ZiC;X{XkO^Lb47b3JaR7=}7>0cvbuN$0O8Py(v zDZIV!_4!-x5Td!5-bv5`Ibm1UHK*n0Ugg$n~E9o3Iv%fwGJ=7@eWbP zl9+r$jL45nX&BkKa)$W$5i>4RgcB+X%}yAaByYT!=vtu=2os!>X@&ZUZ#{oUFi;aZ zWE314hUvvk#$vs_C}RE>6M&?{VCGHF7A#%nwh#(q`rU96Cmw&uGcX)8VRB$I(B+B@ zUUA1}^dEA}?e)e~9a+dMq4M1Enah9l$|xz3g;rlI?PW+rkymImOKJh@8g7eryJF;=iMI>V zj@1SiQ>5GqqU;(;SJe+`P%WpU*?Czssm!uOjY}fDV}s+x;F{@NIn1z*$~1`!aU5nX z3s>yui^>#OFkOk(E$c>m?IazD>dQhA9v8-}ikh0cmnj5!M5_wo+BJXuE0n&~UYK$^ zoTwd}9ce&|v)1jac~#9XOe_Zv(k9D*sy~97{J;z~Xxd`0tUESIhXa$bm5l{{r}Jul zX)hCyd1$a@a~7Lktx_iNyZH-Dla=YBMwt$o@ThGL>2)v5t5uuxzKM5Jc=zjALz+A9 zez9m6_ZY=L#HZ51Xm)@5Qw`pqyuO;9;ch_Zyr^DI&*rn2tLyWFVpt8Q!{y=6*Q2E~ zIvos7uIJOSEA9Qe;)nObOP);*io>7f_iA1iN5y|~IbR+Oz8x)B)7iPh zpT~n?aX6GFy!X@B2eZRvQC%%(0;u%$M26tJnU1T;>7&8p>ha)_GbynC@pFow*VE$7 z=kuRVAE_gqrw(wBK7DfTsP%Kcyf}Tt$5J@kgU}nIz@fuq>cAh$EFMbn%TYCPoY&K` zMAJ(&J(i~vg~oqbK0X}{<)48R{RCy@&$;|3@O?UXU!#6HyS%E#1jk1*m0#-P>hrhH%F$}|x6ws$Ft|W7 zZZ)mSVsQAK_rq{>^Gd}}mDZUaO=q)W`LF->{I93QaMXYLC9sbTkmdONrB*q*9+g+c zsglD3A%~|jb#mNK=5KUVhldR!cpJ;tXh6ReiirvY$Zz^ue$Rg#oSqg>w|00WTh7J7 z;c?Y!eO1ku#mjOoQ#c+~BVhaRXCa=!)ofh6p3aJ~v{}8qEatBrmUs@x!>2Eb+4!rX`q!$uJQ#e!W(LEvNpbcgpI29B zXT@rDc)VpHtQQ16i_`!0;+wx6UCK6C$nTT7%i?HsRZTEn9qr5Vx_tO>aE=knRQ7#- zda!?|HZg8C?UyL*6ql39MLVGzKzx-zQzl*p3xCGw(5Z(P)ynQww7Y`pc(|ReJ+?)AwYQ)Y zF`IK-G5Y#SP|+a!1sC35F|Y$&SH)>FRBR-D48pKF-M$~J6*SxU(|^49^6#fY`m-}x z`~Ud+*U#n`mx8fbC3E_C@bKHuUcC7CZ~p#8e~}7BUi{Dyv8IN7!T4R1mL zH#(@+Pqb}QZ!q6Jy{ciGj)$!+tc8DlDcs#TUT_1Fndm(Tw-kS2PQ|_LzPRhDc%wD< zp?l8WnQ9H}i|OjDCh692e%1c}Fgs&s^nYochyRm@zwZR?LyzufLKg#U7DBZTANDM$ zr(b;i;@LNU``ee#bW<7jQP;2s>4)#JQC^JBmUCgaU-AL6_~~~)G@1c0TDE@(KS-6ke`Gq~Y7P%s8B$hEIy8 z1G)z=%eWQHE0M{c$h3;YDHC`4XuO>DbX_0tdFw4vrFM% zM$5N1?97zDZD+z9%x~Beir%&xb7A!jdorbO+u3+DJ1>ODZ#Y{ly}z$dgeJSs>w8-@ zT0&v;>uqkPgPZJb9jbrbSQ&9~adNW_?#b$MBq-RjS1Le{v6NZtejog!?t=ywkDiR2 z$+CET`cL)jf8SjlI;D1b48DBvt*nekW@X5)C!dUN+0=_KS}nc!;`R-Rgw|?Fe$`Fg zWR;$mZ!af3i&R#qEYsy_FQpGh8_QQn@St+0GiQ491+0B{*6)9G8+Wq&FiylS)Q(|j zdBh$M>lJGGmFcgzPN%n7r_(>;I-S<*blO^{)9yN*?pvpQ3w6}HP(x3!E=|_VcQwLNMrq+SZD4qLl>cjSlm1e>-N-gNuIgUj=5bWvW>t>u4Iw@9NB*VRj1SQbH`Klgk&zq)h=Lk~JyTjxmroL3W=N2TS-XywvS z>EP*gQH)=W&VCe0wW2xDY$)u;fvrVoTBO=XeXz8lFf**Pn`23};Q~3xI5Ay67H_XE zH=42<>}oPyS?+{f%>+El=_8q=ZjEk9wdKKxT8|n)Iir8&bR;CRI$KU($w1$pKH|Vs zSJT5?)L17~7?NrVuML*$)$u=#@+gV|VZZ0ADdzfk^h%`ZtExCrmxg1o-Q>yBBd{Yf zJx{jHj*&09!!&c!CDeh*T5!0|jeNqveQIB>Q)O4&#dvN+m2A2i+d*IB?sD1E+)+6_ zpB-OJ$K!vpSZB}HR^_q~q5P$OJE&)OZDs$oLe|CsD3@QJqQMSxSMSC~s;TCe$4=N1 zbG8aTLF<*76vu-wOE0xTZd-(v)x*QRZnF{SJ`X|TXx`<^{;7aeao*7g?;S# z+&P`9`q;#N*S1-hJ@4s(;r#w8Tg?&ZX*8X!4hG$W>EXk6Dc$?l%M7eaR{aA!@tzdDgj*f8$9R8ie%h^V_rVK2voQk>1}9ro+#+%5!R(z> zPjWefAKRJu8C@Nl&9?(Bff#ZZ?ElVBLpwwLW$f3X?p^(b^xKfc)n9_2hSS-&_^H1D zKfUi6{t*(iA50I21vkj$?hUeaeAy+Z>|CKc;!@ZktkAlPm*}Hw%vAZdh3OknQ4fD- zQ*%@Oj1+jA%J4P^mSmm}9v}FRE5(PjFkj?Pg?=i`(r zjG^hG!#iQ4vONx(PP!emTWU2k*s-jN4TN+xk8m4Qr1WYVrazpl%lP%yM)=7X%A9V8 zV7d(gt4X;X2nG+g>Db7gx9e9Y=7xXWsv82ne=!}wc6a!*-8hQV2mY{4)4ls!Gx?{d zJ9mSJ5Buxys=KS64s%OIe%w}5X=6)Ge%w~mOzD=Y`t_Gz4g_=F;YsmSO1+*gFSZ_5 zUyQ1eb1{ECeLbb~UVovv^LBpaycx}e>7F|X1%E1KFPC!|J{5vzm_?ny!{dKGkFsXM zx7DK^b~dNyXmr@;0D^RUz^|Hb2+K1OIgJi7gw{|4?#GWVtly!4FCLU z_4erKXd6PFwOd??d@kp>s=vy&j&?9~F3V!HDg~ErJv?fkFlVpNkJyGB0fG*AJd?(S;0uC@hr{EF&luc+Y-WGM^~%BvY5@@y zu~dXL6d;QkT@tE^$bp}L=dNE~yu~zq_k;Ah*%sY?(3SBmK+%Z%&ODj~gs4W!r7_!D6h=hdkE7vJ-` zo&=Z6^V;LQ=yfu{-C%#}Ak}s=t>%B3{#1+)ybth9PzhuGbn2kN#4vPs!99GZ*SBUY zKdemY_A|n8r8^FThArKFQdz!q*B);e(;Wwh;Y|BWC~JDGV;T$$?1MoABG@AZV=yqC zL4n3LtM?}xsI@%p`ww(4Y47FpZFAc8+TFcdeQAWp-j6s2v{`=#AUoPDrK%5unw~po zf&m&`hn=o#j*W9@LUm*qhj#njbV@pQW^*_u9dZXeQM$w?^UbUO7S5|y(%aAxZ4ZGV zjCJjDGGlo8pw-(m*+wK1;E{`4g6!8 z_;xfNblcWr@YzJT{)1M9Zf!YnxW}9hybb_I#@hDfc*;Wwynp-iXgvNBrZ8}Kg&Zyi zgCC2xH0|1A^80exbY>@a+rX&i8*G%f!!sx5VB70quX)2h8%T_B4OKG0(jnvd&?%#T8x2Z)e9R%Z^X=y0#8V)}FbeWd%WYYjC%r zYt5xu2*TZ-E}Ml0F26V!AU)OP=&V={xTVTUGz`G>G(DLcPp z(@MDUgTcdp!fju{i8Lh3yeYsXkL7(hlu}>KtGR!aIOiWz1V5h5=gaYQ27Z#P$MiHp zugSh%pE0T8vi?FXjP@q~nWlU+74B>5u{-E~s^;hCW$|KqJ|pNaiV8?78j6+Y7Ti+C z8xGzT%lTU4sC)#p%$ut6toUG~=dDJU#o0*kkB`q(Xp<5C^kzCP8WNNrvYA?fdNm)v z?WKQ666E{gZ2soolte`E7EF@IC@H#2|px(_1s z`aq#+cXiYhnO9vXJ|CU^NYXZ~eIY14?ofYcxBO40+<(-iNL1ZIXQt4zU4_m~p|4tn zFuC1QD^u!)`THgOOX?aO8WUmt{ZMhxnKxzBd-uu{%b7nhpLP1>9GB?^}(af_lApUL!&x~es~o7C;grI(9?MR-5ss2Ua9ae`%2;Z(lcpnKMrNgC<{{C zU$`L)-6W21C_hs+;{RvwTeRE8kp+K+=FHfnMahyM-3etz$FY-c&vZIIv3vUA`0CIS zZF5={H6#^Vj=kTs->_e{RRw?qNRWS`ob=wgdwTBeSOf}%!mCgy6sn#aW%_B5X^ZK5 z0&yMOhso>3Kk?HlVZe930MqdSNWbyhj>2o69ChpXLi(&^D2|Bm5 z4C$}8y|*VhBmkj07=^d~g!1`s(nhkCj;9%Z!nON&hv|>sPbUi$m~uK1Eg^rG(gdmu zsiU?FpB!QH@>DMC|Hv-6_^CRr7Dfcyf|Ch!0RAa26w|C&4w!{20(XwUQ5cp-Tt#Xd zy6l(-$in0d-mHNt5S6ccr_oU`GHtgCj_yb~kW-urRHZ7l7fMo8186@^gsKCFhALi6 za(Th*Da6wqJkM7>ZjoTv`xbxkCOv!i)8Hj5)s{apZ1PIBScW|-Uj7Ox$6b2=l))5V zJ2r&uH3!`;g4R5C7X>Z$2CR}wkb*T33YOy4DvnNg ziNqL32I{wO(a%DVtieJP8{vNM@v-;KzW986Z1?u1P1`W-FRa|SohN^!S%1}|D}(*s zHwK8cH_<#bNDM@38uv+2TXIN+L|rRClP@mGrmf3f%Qt)D{>Lr(s-8&H=b;S=c*g5# zwOZvFwf<3NlXX>GgW)O*HvEdOu77;NJ9CN(VJN=0V&N;aOW z!&{s=N*Hc^m_7a{LbBU^4u&sU(S#w5=A(a`86MZtn{f2|lu0Gs&Yrmn zuBVF)M-o(NE)TgWmXywnp!$+m&QO+b;WT7o|F#3zhR0*(tdTuDK?BA+KMo!r38I`a zu>O>n|CE>il$ZaMm%oy{{0uaIipzhB%l`@Da-FFA1q-bt>wd|q|NlVPRZb=S@9Y|5FnGQxgAE>HHrchyN*Q z|0%a#MFCd=0ll}jM*+J60Ux(=N&)f%0S~ujP66QsfBuz3Q}fC0U&hg00((mcqV4e; z(|&xEQ{Ie0Mzsf)6?&=N!SFZwpg2z0nfOTxXJ$I~V|W}Q^oSW`((ktbY@MeUjuwO6 zXH9z>#kZ(J=%*bZH<;oW_%Kx*a|;i|S>o*ii=JPYPFR6ZImZSA49bHfP*fgsi*`2C zN#R_tf6s7KuOvT_Q<9T7x<*m=h}mw*Fbp8c;`$Uau6RrzH3rQR{HRC}=HhVdpUugM z2q|mu;O+wh+zL8rl=_p+Ubm}rrZS7K^Vjq>-d2ERjDl&w0|}shi?XoQUD;6!ciOFO zuF_ScEP0L-7=3&MMR^m|Q*b3ku&7tbP)Lg^S%u80P@Zt)2E)5%%Fb!zGh`OJX$^+V z>-M=E*VDfaki zQPhj!Eesu;Sv_r!{1ca*RRK*SirC-2KCP1A<5mJ)HUS#_QLR1zk?HF-T(@ExklWp- zwea7U4ORgn4i9b#)n8f$G!@K`A z;?1~6lFcHK|08sVoDD0C?n~&#wE{>qup(Ota^>(beWvu&719rUe~1bs0Z2+MAG*fD z6`mb}Ob`K5&V!2%cN{~^wHTv=?*kZWV+bOK8k3$@ELl6C=tTPygBKeql(=p4+olcJ zeQ*O6Tst5powiKcUWxhU*>wBD5-kFC2-My%Uh#_52+_R_v(LmfO`G~w{D@oaTWAE) zY ze%xx=p)O>&3Vt46bi#29MKfK-7xjD@A?E`$6ta=dJAd^4fAkkq)WHJRH2K#o4{2Y> zbd@8EbL(d~j>aik*<8iZEtBMU!5*sFV9S~XX&A)2WB*e)9fhX5r=*+iHhqI2JPD_! zyCZ%59^PJ}?fNdICASgq=`cWbqUnA^X{3&H&B0(G(KRPE-|(knG1aVmiUly#0S>ho z-vGv!TV3#ef1X7NH^12Zrf0eZMW&~1bvC-Tlh-ba3>gYk2Tj9NwxqQRH$5-*x~4l0 zV_Yi9*y~Bt=8N$5b^tHztB`vQ#lmzFNF+ZM7pu6x$Hoom0!d-TJaUqWhgP{S?awfm z=9ixN7S2w*Gtt~^pj}yRFFRAR^+$ziy><6vK7)C5e8nB)4ta)jQY@0ls#|^WWydEx>lNcf@$7NU4Ih!(-J zxDKHu$o|9`k5#}CAjxfv4~ZePGk`**t8yt~tW@tteRdmUj0|K8V8m~N5e06-aR9xC zBSh)Ce*$?lttQkYFs*gu_+t<+4N;!~6(Xc&fCr(7*|kLCh;6s4-wMhow52VgZ-|xV ztG7dX&gADXy|K(q`k!eJ%o#N3E{gHR1qw&FZ0ZJQfL zzI$bqvF-BrU@@Q7@0Ky!E%&#GJTbXxV30_re`e57sv#+x*anmuX0tpWz2}X&evTDl zt~Ho!jplNL^}&@s)IE@1yYhufh|86VN{GqZ0TVWr@#sr2j#AFUS|$Ur#iqVtWPHtl zi3~IKMw~dj4cHV=1kzs=KqlURN$R3}i}buVC&SaTgCVM>1h>|!xrE0_{GEKdmfI$& zf6-Cb6|_*bwSjH>2(wM6#7Y7`9^I4_S+-{MoBYmpkS9gl;nPKtb=XeGHpL*A;UDM+ z6nMlwmcl-k%ECp8!Xs>wj1{F!wh)#PT)=AgYPVmiLQ?Kl?$C7kKCk?rbHI*=YoE>e;>*n6+%Z{kx@BzRCv*OD{ke}=!fVohz~)G z0YS%{1}QMi-_13Tz}MI552@Nre}Y%j0p8VT>gcqgIy$Z1{X=w)c^OP-!%uU4=?bVz zTX3s#7(-fcWyV#k7<2J*S=M)QbwWw=nm);jSBb?B8B39bJG|=Tla=1;;x-!7e-0|< zZHVb0p%r$=d=>bp?U}*|PfI}kDU5TLh>C4I8lm*%qtAi96?#5eDQOf^u#KUD<`b~v zvBC6`e94-xGa4Ts&ExE2U2XFjy`;HI|l$`9QBksW7h&>_Qez0R2!C8{27~KLB z=)MFxSnIiA71_ev3mCSjr)84Oe;1{2bPh8+%d53r^Hgd{R`yk?9T}4SQWpH-!Ul*N zcwSnaP&ECr61zySlrxF&8XEI~Z5SJ(z72GgU>;;`z^4jO8qZl<|7C6Oop;-N7mwTU zXZHgBd3o`8-tAos=qLR%us?SGd*J@MWs}*?T2H4LHGGt!VADN5K^r(|f0AL{-`{tn z|7^Ms(%cS;Wm>)+%q|AQnv21(^XARThxhO3UQoUC{xLFGp2_?=7_>-5fzld<6PM$| z67`E9eIHk-$HLoZyc~Fays!l5xqktIZXbBSz*5w_>+4l}i+;n954G7*-FE3li5fw_ zo_QA~T{dZ3!%P)m!wZvQe~z8+(d?}iY;B$Ip~HgCKY_DxO=<7q1U0Fh6Icvq^y>_M z{nG;}37`091BV*o&bvW~|GLDi0CIpT_3dxjDretawp`c;PIsFff6pe`Dl~G536OL7R7k znPZkU@;--ScYGdQ2sX=4#%{zDp882=HcxJ>k^S_pP>{u{(A6j^QQ_XH9PCa8~3F9eUFS4Rk=-fxjnD{agXf2pHNAOq_|t!rHY2*AQ{Es ztY`+XTBYT>_+XYK2SplHFG{%v)>g{APN&S`l9`|A4niq6xBU?XK!kv;CRC7|lI}#t zQ=v^!QAzrgBF5uUq|l{3ziP0Rj<7n!$N-xlmoCT45?UOfe@Hn@qaj}slAPwm{oL3W z&3chTlyn}zION#~Y)lRhArzK)dlA~6E^i=@c^+8T)g^aLTbG_IFr~R*O2N8R#>LgR?LDv<;3f`pC4oVFn+Yl-+2-S-pH!NJaINZdK z|5#J50d%4`e|ykOH}--8;D9Zbtlq$;bQ)K$X$v<~URF-&d2SJzcPPuk&gNN)Vn);P zWy6xvD9S@;NT^MF^}ETCuxqX)6YZ6xYvpDaOjrn3G{diX+9BR?n0Biq9m4*PZ$0gb z;}+Mb$;;R)FBv`y`%PwHynF`6IQz0`JB-&%DTCL z=K|f#k8#$Q&1I-jD#a|zxb(-YCUvBpw9x+=grAqq)jL1tR)@uYP;%6@!?RXfJG@Xk z39qN7p7Ok5pG%{HZHjfoP@&aG5ieGjFJ*2VuMk^aohVdk1pKN}&EH_31}V1#hBj%e zc$OIXfACIJIH$^63!SUk!j;0z985;gKyIiQq-RyO^bJxzptY@DKG4@y1C1xC>T|9* zQ0Yua8k%;o(~3N_a5}Cqswvpqlx%XIV{}tQqFCKjbD;l)fd>g0+fbbPyCN=~u(Q)LfJ*BSQbV(-^oUa*%M zf9zew@GgAt<1yJWcwOh#e_$pJEZN)3jR{l96opx0;~Zxi^qfZ0t}B3{eW(1*Fl3?ERZq#=$b#AyMd?^u-yK6_-O;UYggeCysDfm^+m~-ir#~ zxk))MM`}D%Jy(sHIofF&%(C}AfiOD7f1Lu4YSXc2{w3(*|Iro(!YnUG&!|{tZ{8D-vZG!nhpQWSbxm;!%sZgqF$a2^)-EB&<<44rn>OY_8Jb`^Xw zakLzoT@5#QVno{wHHY!FY_Z{3HT5ca# z12eD+oL{L*&XH)^#j}~Ea`tjLf0j`wR@u&7a)>t0hloFiWIPSHv?{TpSq|D92(!c% zKrqIaO)t7oF!R|)#yBC4w4L3C!puYf0j~P8s(2zR=C*jLN1xDEQq@S0?zr|*ns#_I z+#!<&t8jv27-+}So4;FyWrPNuE8d~hdoWiAJ70hD;}W95U_49Wda%=kf73bLn*fPy zOsz~zYPHIXlS@iHm$|^B^OB1OyfBr|?#ym&ZK z)9mLP-`8NYhkQao>1D+K-6DcJHi0`DKKYfU^)Fe?B(Qm&?*NygvBpe`xsO;OtFxb^HZC znE)W!tSVBI&SADDAj0GoV*B#X?Av^J2&tso$Q`$!1F^Dzl z=i|4>ba->j!ZFFLe;$N`6Mo);gAyt5XAz8wv1Pz%&G09a=q|yo|5t>UyZ69^abgK$ zDqYv(Z-_{J|KD zgD7#|F#Jz`IKkmks!lqU4d4@$TiIqEAeYXGJ2^N#{rUX|e@38?gbUq(N8Ca$!0N@e zE1iMuWQgHdpEbIMGIXX?rV0w@M14JWSEhr{!z2I(5vB`X`%h=z56^x*d1I?RB9_#4 zVX0M~_vJY)6|Y*698WUN6f(FuH-z&Y}U%l)PO=GD8xqp z_;h%5{Nc?ZI(5$&5g(KCJ|uIBBe@=OcFJGmz`{nZ!=y5-jm>ygUsl=h@cTE1|EQ}$ z?$W#_PQBVBXd_`i`EdO1;KQ#N-cP?0wWay?$vDKDf5Z1b9#!CwxkYK6?C95tnBUc^ zufizU63XSf*)A$%)!DT31)>{bs+x$UBOVV1Dy3wU;!MYOp7>#$c)9xu*@(K`b3zZK zZShbDO;SHU^;@lUKkVDw6-Iy-s`0rGhlvZ%Kkr=#yRY~OO*>QasQ)wY*5vi#JG3RJ zsO^t$}jD8Hd`>A*$lAfozcPnOk z6x(jfex1h`%J1A#&7pY)$E4I#J$mE@)7my!X$QBD#O6;J!@Lc#=%^5?a;6@3)Z-&Q z_V%Xm)0x1Z-h~sPmSu|F3+?lO&kn4}OIj01f6Io+@;h*D8wFO+CR18(^7z}`ZCfdfq7MeYki$?QY(bd&5y-!-8L;LZr{@saM#c>w^Q zK-MofNC}MAuj0pV;>Usb@ml;i6hDr|kH3o_|DYd_kAPYxR3!>JC_-0fVia+&SM2a>EA_ z(n`zh&T%;l!w2%9m)SCBOT$8vnAtYge`19W(2re#7_wwfa>>QreL1l+@+{_5{zGv6 z=6+^veM~k#TIc`u(Z1OHXm4Eu%PMB~JDZpu7t&jpD7TF-;#`eP77*E2sC>nwSVrr| zE$eTcP5aTZ2JXkde*7!^!XM{;`{AJdTeto4FBh9OB=Q6@P5#*W*#6kLz)~MKf5}$4 z1*C_!!rD<~9nj#E4e=Fl^E8-xYip_a?WCP)bdvQM=1AYn>ec&<%-s#1(3Dllm!1MJ z{)6_exoI)8tpdKue@wj*DzusL_t55CsM}BWr}<3aPfU<4CCHX0s3l6HpJub{@yFPk zWQ)FU{tee-G%)|l|NcmSMT|Q1e?vJiH;ZZhz?yjCg3or?uMRW5r#OxiZ;Y`8csC7r z`xwuTTEI_~l=H}$Tmb*^bDVB0{SMK1EAcG$7aI;cVKjx4uVtF8s8tUlEP=9(LUyqU z1twE3mW+6PN*pDg53?EmuCL3}w7c)IMUT@FultWB$FMw(Kr+8f(wJCNe-XmYJwU-C z)@_;Tf6;c?!FK_NzMe}O#NfrDOnJ$-!Cm+Ni;h5n-Tqu7G>Boig ze}}Zc7s~$w(*6Orb#Vb$0S|NK|GD!2T=`$L@~c<p9nPICei@LARWu?*P~Q%fTW3AK*;m==Yb`c>$mS@0WOb z0p|gamtcDV6#=%lc6$Lv0s?=lm%e=g{sMo~x4wP>AORHJ8?%qz1@UIw1@7#u-~a8s zbAIQS@_zw16uVQzjGncWitnXagTPOx(4c$o_qRTP0cQdM2Dhey0Zah_khk-L0RaMk zU%xY<4q)A3|B+I`!;3w&zaNhs=-EoUFbFQyKTh@Kw*ZzFv(F~w;E)Jrob=VU%S64P@(nt z7q{#D3jh7)cAp5QoR8_h7Sn%a=_Py|kNHdc*On>grHHE?LuD}oXz2i?{PwSZ?Ft0# z3`1LwK>%nG@UohclvBo;;VTm?i2bcp=r>X5w^lg|bDqwFi#|wks8nJ~m>~UobbU&J zAtP0<#EeAnRS^CI$2a(oztIi`7UZC~;dJo7o`XNSJMa(ux8vZC?jHOD|Lr+@gkpOy zXv6QIz3cFT-3+Y93gYh`mWM)rJwO8gcAXtKWSYP>U8)jm>Y;?vW%cYJK$mK9_PX%@ z^x*cq5ne2Sh!DXZiao&&Rt3|xe~v4o+l3Q5{_XL<+x+hi``hFBJ)Ym=`8}S$&GWZ; z{x;9w-Z3vUt`_o;y}7^~o~IYwp``DX7i?dg8DVhzd?)ZwT1VIX8g~wVjBR9_Hj`BO zmJJfyjQQX0!}bD*4*4;jL@$~nu$Km&7ej6A{nFu0IH8vxw)7N_8DGP^%3hgL;}S8; z$_;Tu9yi=(Xagx-R8OAvJ?U8FkH<<)we|EHHecvVt9t>Yzl^p*R}hz-Pf;ZImYcW>lAzrDYiIg-EVkN}t4;d*r z_Pjz9gpf9g<)*`qI%V2rgyTrUXMZliuCJGpEH&iohrA;y37YvJz-0uLyH2G@#%O;o zL$9xw(|D+*YZ8~uzr6V7>YEqem<|e*=3lP9*@gczo$DA6r|^k?|7R-W^H-sR^YH0%7A`8{SKjRw*c6qx-vLi$|RV|P0N5zSnm)JZ^h;0Lx5WLaC*&T zR#L9VDW8tqH_;JA7DERns;)hn9Zbg`f)T7i43musL?-FcNuI}0Ig0X@Njcv^#Xxcz z$QFil%u54Q3&7@o(d%<_0ZB6ErUE1$$EX_j$}Jf5w$SCj+mo;LQIxJJ;bMs=#y3qE zA(QO5&F`ZL-c5vkwsw#Bg0jpG@(CW*Gch_i^H{q~#}`~kg65DgJI4mH*;`Ro4}Loi z38y~S2LZJ$UfVZ^n`KTqe4A@8btjM8TR32~ z_#sGOAHcPaf7yC0QP4$JfK)~^)N{%!M5zk8iZ}QB2#~U$IQ5>j!g;N{*~A4IeR7o) ze&KojTGuLnyhaW>MNW~0v({x1Iq!CWmJ)wC{MnRgez&UyDnjk)px)ynxNM^G{PtJ? zm?iFJKV`(8&H}V#DV0tz-E=a4#WJ|vGK~)>?@-XnE|&Wl2E3AOM_28!zkoF;U98d# zDu3wVtCy=C;21P6*0y7IO}360I@l2qBW(@DtOehH8d%oU^K%>RcE-=`{Vp0Od1$-g zNpDT2TOiBYj*{|Cm(c~_*7R>4eDW*e*3_Ny%Pzi4NOb{`ZdaxA4j50HZU;;!b7e~T zD6(2+CT%&~3zO-zZqp_M)rtPn zu)%D9ctuo@{Mj=0i*b9qbsNG`Ey2;&lLK!fa5!baAdsvDWCz2F!3KjQtl_h6#(CIJ zT4ZbVkSHUa?BN`_wCxCu7=>(6va)!o0ZPb6v44lrW*A3y-krQZ_ z3?S?4_;1C*gVV)DT|UTW3%4BHy)l+jwnR36W2ry()4{fjk9=&Rqapg+)zK>wKYRO_ z3yo}}UnnTi3`asye^?Pj_n?q6{Pv`FS`7H zm-qM6_dj;Xh6;~Pkb(v#K{BxN9!oKvp@}}XGOFx>i6W~(+lzrTk-%V>tqjD$5*P>S z@li%)b~?D{)3%@0)=5ARv$Splf)2H265)FideRjK7%q<OCgOeD4S9n@7d7DadY(T?;VUw|b)coT&zloa9@cL{( zVV;JIW|%{E!{+SOb`d6Ecu$s1kMt$r%k2> zZDhkpw|rxp2bjG^I&)>NBb8rY&p8WLBBEHqz#+R}%*9dUtEcRy>}@vLrHMU%O>dBa zy_t@)z+mYk?Y;eD8_p{Tv ze7IFOXBN)VlNt|oNals~0_S!HyYiSUx2%vJK?!$AO#e?zx}86P#kXxtHyp~h7n{Ho zya1XwurT0X5BAd3!+AUCx>9F<_)cKAdZee38imXzGK7EI}XWp$y1qt4m9 zc;H+GB-m>1l~0ehid_iX(@g-}$$J%0!!0RAUZ=9554%JfMa*2bQBBxBAhoeKW9Go%q#Z=z>xqM$Y zT8t)v_C77&$qNcjbPTL4nwAv|dM?a&@}9iD9%PXs(dCqKK~*T};e``L30EQ)%9_G_ z3c(gACH(`fmR(s*(N6Uu`<6$Dv-RxjJ)f;$p;=u1)^ z-Go=^Pct;qneX} zSoxb|ah<7~bDOSz(q9uOo2?r;$KzzQ;IGV! zEsTX*-;8#tNUx@dc#ozU1U+dcH}I-2nA&njA=g{dO+8&VVL6Gi+i~~?EtY^b3VlEy zB$;8oz)iUUQ6MZy*zLcQeP2%Iu_bd6kd3F%y8PHJBzYeLm*bWL`z3M&ZnsGf+m+zu z2|j%}PcHC(lnMZN-0p;9@`Gnly3QJq8R7a$4Ir8So@Dra8NaW=j}en8eI(2jJ@qpa zI>lZ|LT8`ZgXqmXgUt4hbytQ9&3iePr-BL{wejpo2BU+rV;9&a@NU|Jala0xAZD2` zKJ%IBNF%5e>NPIJP5!jNC@-3h!kp_1Aa-fG7*tT&6p^HyzaO zxaKeBQ$6HDK>oT6@{B>%wEcJV{v9n@(j^vUWz=!>Sx{=`u4|YE z=YD0NZ(1S`+()n~`Scs?Eb(^LyKi=)dxn**bw zO&{I(sHoND<)ULa4O4lE3}^l{ptD{+=BfvOLrc`>)Hp(X(Z&BW9jW&;gR^6s((K|3 zuuxl}bDdi6i~35p_x6@nDm=@PwVkN*4Elxm1?&Yocu^yFic$g+iolXiLF(Rw9_kH6 z4RjhDH0*)i(jLI+Nm1q8BFdtl#Uj=Ua?6u*C5qK4-C)A3M7fH+q6M^WRHDmMI$vCW zbu?~ww+l2PrV@QQvI>&;!dym_yndIFwY{7y8J+sFEl!7QSB&NAu^VEp0j2K&BO2J5fWEc|V z5DJNEQzgdjJV}kq0RBI{0|DLWCI*~MjF&GHV{HvD44lgKDfMG{it;`*-}SnCCSEJR zt`Oj{8KkqT0M2`(Ai*mUeA&ii%6!9EUr**UdgA^;B&o;;_^$7R=>~oC){lW@wgX_E z<1u?NPbZk+RT$si!SQvVe|6M0vn@1;bpls zU$48{M~Q?Cz~OVZ>zbVVo1D=>B0ArN-ZNVvH1*VMrK}9igbbWLC8US2_!>(0z$z6F ztYqpXl68$!d<{FHw`IM4JbXNP{P1{YeH?GXV*QwGTIa2c!Fjj+@?!HqV~71{*#pD+ zxMl0JwyrVImDXD#dlrdqD$$R2Fv44(zA#5fF_Oj9GwJp0E3+kCKc?q97qU@_6QT@e+OithtPgHlU#mw zdJg|S^k<$KGpl7tk{y4VmoPE8gRdET???|5AuHOlh+u z{PXnWTmtYhMXp@y@c@!oTz*4N2+@b3YlT}WBTDWaw)=l5;9d8yagCIGMkDaHY|k>JZ8mW3j*#T z02Br!P#U@hg!q5)w?>*MWT=LV!fEP#esaX`4%%dw%9jBkSUUa?0;|we%HMPFv4!(o z;XTTZ-P$lW+(3Dta(ioHtE0&;{3SU2{5Ai1>VFFIuMhq>oG0El>;qc=^Y`BNW}eg9 zb;kU?i9+9O=H=am2Jo!H`?H9yYnMTo0Th4cut<(?(PjxG?A1!QD=ZM7)yT( z3az?_(+{u(TyEJf)-SM*#Hj0*4FlQ?S{%cZv#5q{XV41)O&c<-e zg)YQ@?ZRZcy9vp-WTwV|kSkW}0=a)b#KuxIq`q-4iXbF z1c}+(o!uJ;aft68ArW`)1N6Y^nWKPA>qP2n5NpXe!n};qG`h_OoL>G({N9pjgSoP` z?G&iD%Q)%5?lOcpZd!yCbV(te`6n$^BeiK6=@@INTn~EaS8L)vR-auJPE@F>l~z`! ztiz0HcO5?LVg`x1k}*9laP5ByMxS$VtJB@xM0@+Y7`0mCBPQS%%HK`#(`fzS(E73lI%%TlTiHwk+>Xa%<k z6yb#v>|s1mSVYmOLtsvkt?9FooDi2gk^9%dwzOv>XwVdYFYx!m0c?RtpWttv9%%>mO zH!p;}LL`=%v@rIRNG zuzT#TLbjL%u@_V6wiJ?1Djj$sl`drS()|W=KW;SVgGO^bYBa~w!rVq5g<%@{6Lb%k zpx+>F<+7X`nBR4!c|Irzg)-U!NrJgsw6l0;d#=usZ{ESk+9wgc^LpOzrd4!;(#+@uAVI1}3$ zdohQCdBvd;9)U3Rr`y)VNf1g(0bY;OOywwKZ@w0+ua99VjayzOW4rjuavxtJjoW!D zTA_QC*z$PNrWJn-+S~3n-gv0}yG+t&BQ*{tseg78q`o(9C&IY{MitC+>{q<9!FCse zijPw-&)k}phfzCB=+PVxv?*1V@KC%{3dKulEpMC>6igoI+E&DxJ&F>G@)-)4Pte;U zGr0U!EmTj(CxLB;F~@CjdB!(^h}pFG0yMMx}SnUU89yQ*@34 zV}r-$HPYO0zc{%cE$7N7hhn`x^PmWaH?;rQWXW)h!>fMr^#EVn;_C??4NIV~OG%(1 zd}W}0))GWc(-_d=Bu)Wg+<9n+Pq|*kvK4tEa|PtZ{h;8FoeXrvn{K&~-N6UIZ{z5e z0(hM+rXznk#jB?+OX6l|;8D-YBtjge;sRVn-9pa*d$g??cO|OjQCTN}cPiYUJwFrD zDX=q}7ZeGYL|L8(-{yCH6YmL}|lxWVq% zpb7>0Jj4KFP-&s%EZy@&qA(2-ZyixV3g53Z=wN)z{Vi525}A4KV`eXe0Mn(*Gqsh7 zkzIeC@z*?*PaIJIj)zh>gKPltN9M`^KZe*p?1#RSSRVA0+6tDO_W<6e%cKNoJE$+R zePQQO-wq~|+yjc@oBhncW}ewM$*oA&5q|agUl>&=ERW@9_{O;FH+~{Twa$8kVyxty z;%K4y)LX*ZfJGaJ_5f}B-bk= zu$}rYL;L;pbuA1%JiCb|FrFD{55z9=cX(16TyE$ z%L6B)InVm=!eJeO*RG{9$jZxzva)v8p;|!f8E=C{A1O>N&)jHbXj&Vl6#8hRRmRjZ zGuy_x#jn(?o4j=4$HS3C)9Wal;#6>W&kT9amCi2?%c~1yl|kfztjGz=9U&YdM#WM} zdsWTd92ujVC_#HBZUc9HbypW0ggJjUy1Hv$l7GN@P^7XHxrAi`2Da&DMmCUPqKD3! zE84uBy#JsJhQJ&q4VwGr^knDA_=(Dyx$hX34pqOM%){}Ihv>d%`CaiJNys~P35}lg z^5A5W6#xZFWW|p_#HM%VpVgT*s5fMIJx>;$PN&L-f#Ej7#Ng;0V>Su=Brt#O{4gaG z23Y#>qJyEYh#E$u{Hlj+=^0Tt$#Q2FUUBfr)mFbaU$m17?*uqs8#JGbZX;0skf@tE zR~hsQwN0`CrlHlZb3(yI;P%^RZ#$dlE<2a86A4 zv4ix}yguge%4e~r@tnwVTNRA4MRxy+ z3&y{xZ9yDA$rBA2FfP-|*?+?3G6!3-2;TAqZwvsC{Bag*l!(#hb7v3c0*2r70vmqc zr0Hykv5Mx?a6~VZhdBF!vHeBs_E5%{7)Ukm)ycwO71XR`iqk9|47Gpj6b=P=WX1TB zcu00wthQ7Wt1Uf`=}p6y+47k;KJ(PpKSp$F7$cV~gKoEV=4<3msz1=(^1(NQZM!U-x!X2Sp@-wp9(IIw@}`6b23!!U&yL{Tl; zGHuhzlX#pf`*~~8U>%Qf`0Q2*siW5Hb#1p9-A+|Zz%L{akrA$C@dwLOQw_J5aTPh_ z&{(qQ(P)`oFOJ8mq{yB1()IEPfQU{mbjx zCAr)d@wi-_#u;D%C5I-R%*dY((S=)_4)4MTKUO^%qUW+C4QA4FX1ry_P{V6hySHEI z%PV)*%dmeA$27g|$H!8I?D{3n-IQU4xgwO^p2V`2JtobjE?{%P;Td8Q9?uvaU&iAj z9@(>5sP$}SNUqJk+_M?=x_m+9eGaB>etC6zcHo-Kz7kJREIz}*iEAE^lxdux`LpS~ zK6B02z;aVGvj@`9ZN7`K67dI|=59e2OM{N_A@G043nPsT7OSHpKzjl-oVI6N_(f=# zU&dWHnHcE}Z~!1K0w25}6?|l}u8^@Bhn> zJI0|u-AH*+0|Pls1Y&__TU3a15pfXJ79_y-^E=A@cxHp%wO4J*j@lG{{sT$?DV2w1 zj`)9Z>!r#ZBfK);cm@j>m-mu74t_ZJ@Q(E}%yMIbHhIR4pI}g2R1=5S*FlU861|j# zs0kB*{ym^$YLXh`d4Tl*-$13W~7NWyxL)sNkG8eOHl zeny?~T^nUDgPCm@Oq*u}_oE;fC&n$O2YP??Q!vrc2OaK*G!_(~1_~_jc~9bBAcsr^JRk^W9InxwD`y2m?}Wl2Gf;lxr^f8XJIgcUtxN~tEPWB z>;u(fJ_H8TX?(ZyqO(nN6o2jJwS7Y?ncB^&I|Cey+Zen*h(0)$B?g&7L9BHKKnLs2{$BtKg(mJ)A;0*^V&| z%2bJxj*c!m*3gpY3k=2=78D*Vpcpw~G0>Z05Ys^V?7qKa{J_$%!-;ydvd{715`GsZ zM29jprAinkq-hGAG!92Rf!PC#xeaNdgc#Q!#vd~_{<>i#3)n4h4IVQ*qXmB#(DgKY zfa;KSv!`W&gajAUkHNK1^Nm#sWdisb)4atiC_yk=%>pjRv6qh|o2odT^Eg0eFBUWs z!~DiTV?$p|k0i(P4fWGByq(_~xbx$nCBP=a+x!XQHH9pRp&x;+op}=szdUb_9R_>WVdp!@|tw4sTh@B%)r(eJnZ`WFTw~(1U($kz?Y{ zPGHc|>(X4#Moz&I9rRp@vrDIujgex>3A(n<;i$4zQM{063>Q_ z`B!w!xSBL|yUKQvNR*1c$AyI0I4|%gNhI@qIn;Ydx+{2|H^S&_% zPc*_g4@COLOn#yJAlesHEVve4qlppZUPT6jjnnx|ZMc{=l*r^!*5AMhFbNxvQ%%8* zYvk`RVoNq_1QETN2^)Xm^|XNP8;(r|okqrnw?TXjx2SNM;8t`42P^D|YnogV3S2Hf z_z8^aEzUZ0{vYCmJ^A^a!8~tCEDYz>Pt&aDv665~?1&@IC?V#DyD$mW5(=79xcSoB z1#!Pr66LhS$JLG(a0Z6=oK@q)x%3w222GX!+uoP#$Z=(h{Yrl@+m9sysZxwNRl6G$ z2Z;@mDu|lMg@LH7C^Ds-l#y9t3GRiZmEo0V*el!c&hQ5q@XnvY)%y#d;m&bm$jl<8 z2XH}F#&m~s&pq=!=e0n(n_|l_L$L%$S;F>&%}w%-{y3>@H`RrPL{3(d0>nI*;Kga% zWZ?eRXz2}%gPDIx?R+wlrq)Q`6i|sZ)P8|T@N8AzIMHusTq_msA)w5=0R%W;t7^Qk z-4;eigervDl%GWGSm;&Yfbr_c16aUv!TxgDg=Ba*Y~yR9g4y`=4M|pkE<7&S8`DJE zUsWQIP^tMWf#m?nF`v&@KMQ(?71~MiiloSlC~r{t1d)I9UM(Ddehml(+p7hLK)_tk zB}DP0+y|qdmJ$R3APCyqw}d{Z`^@Dyjm zY9-meqvIG*cYFh)4#W%fveu(|g81}#nw&<#<;^l*fV6}0sFIY5K7`BwDmaioA%3Eh z^VM!cSNeYg1ewt~q98`0N^G@c29ysq8FRMvfY|2)L*QM&6t?<##LuUw5bBqZ!LWk> zHyjEcfb06Bxc(z9;keq|=(iJo`#!#{9&52uznB^?Qs>2Dz10m3aW2ZjV06r-j{n}j znhW!V2;rdGU9`)PuyXiF@C0j?nUDJ7?dL*#Gu|Opx70hZD|Xf!e4{U&P5}prOw_fE zTIm~ZDfcD3rQW-mm(QvJIe%FN8AWon0b&Y2?@p)1A&cpw@uI+gj=1K^m@1n_*f zZ`kLHPeHzsIYP(sx-yV!V2&jS9+xN7YDp5BcQmR^8k5gdIewc27i0}-!4 zsS$YU-QpF>BxlGER0|*(m}9t}T^8QXilaL#>NRp2negqtDR%~isM1PCUYIiNiAzQ@|ZBvNGiO_N(7h2$2U5RGp+^Pe%^Vr&J?W}3# zZ71uklk~Q9QOA!W^s=0p*i&QY=AHVk|A9J)0xOfX!x& z&1Pb-N!P(Cc z!o1vfWQPsx<-X%m=P)MnX91k!-UaYiU{7Q>MuuVV2@K!7!R>qB<^fw!Isl`ABuwlN zlnt@&OZ9@)1nT_dbhDbzC%p%9n#d@LK4bg6zRLcLqPO_j>yy1tm;ihI&yd)h<6!_a z#=s4c8X1NmvI=PTKU)D4hHETWmkzA~B!8O|!0t26K^aeInV6^KjBA zaRE{`4DvNlKT#N{&u8EQgx2P?H*vlLIq;{QoOgjXc~H)9DK>>{VFzvCS&y6Imwx~& zY@?t{qNZ;IcUpS5<9@g2Nj{|WWiYi{C|8I+)9tR93tX~01sKTR@~=A|9v7XNgteL% z94>vO<-JM3SdMSNhw~GKzqp9h>E%xM1AA<{f^R25O~Z)x-P#oFOt!Sd#8J`>#yCxP z-(bUcbLJj_JZ>#Nz<6}x(sxjbzkdOlF?jSHz;T1q`>TV%pxY7V;+MtdPVlYT7_+Mx z4isqJ4EOQs)oeDA-N6%mgIrHIAwU&?7fRCr0G!dcxPN~6L*rTH+jA4aOR zO=&zRNeEm9ABCTwl<_e_AAj?jAkl%()a^c-hdzq4Pf%>2_k^^s@9|5-S0}U-wX6|X z$|G?kRDoHDiFohRZ@zl0;ZIi}r4*YBptfXgARQisd)4q3_Q+gaVe@=I8o_HJeDBeG z^>gr17Jib52`h$n0TfF===Z*0U6Adxs$m1c#1`y z@qfI)laojukd=v_z5d@;v*ko?b_Bu`tglS3%(gpYLcJ%_ZE_7$rEOq(!3i7b2e2^` zN~AQZxn@aO8~_@@L)gx&iDE%^mSqqFfKi#1$~AogMWMco33EFpMhw#+WZKzfg7UdVh2?kn)W(#ZK$ zaRqO`Ci1%j5SFx^tHB{^85j9xgF8l&L~oQx-P`(Q-&9ib0B2=7q5RQP-*}&4P$=({P6?8y?;yOHCHpz6!@$bG_s)MUfir% zGX=heD}$5ZO)T|@$v-A!sH7kLuW#1a75ooRs|hoJCEWX4V6lbZulmgn`DMI3yFi07 zP{DqHq$1yLG1B4})bci3=po@1Ak2OF+M$6shofOK6;6sKp;toxLM${mh4uZOAM>}( z->5Dq*?&Vz`+7Wd^04J?Kpo&U=3Gj)PkjS2$*hzQCdz8@F9XIK7X3hvfIDAaP>$BC zUZ38X;Hxa;eYb(74FL3cRX!CKeMG4jh-_c(X26XxK|3@J8twu}#7{foIZNF7yXg*S zBZ?slmPQs?z;^s5_)S4~`Dywq4l>C}y3! zgMTmX!?HXeR|%~qeKMW!r*ub8lODm>IM;Jf=iU_6uEa}E^YTaX^Ow*DDPo|#e9|&Z z@$Kh;2tVg&)D^$h)iC0)KrAIQHOMS<%v#{nUNx2rGauK|V)w z4)BZmHosisDcvu3cyO5vQ?C&&Qz7}u>whb~iJZ2WX@p`q%S-N}xpRybg;CmQW1iKrpTuPQU=qxq$94WviAkQ2VgW%cM=WuO5 zkDccMV=0{0vGY7}o+tR6eXNhZBOyib;;C5w2P8hDw4akoV_8O6wg!0aP^+Jkx`IND z@zsB4E5K?xWZ%{RM)VM;OJ^qdR{zIRJ{z_2&O zmO#kHauLq8Nk$NKZKo)xGNvuz;OL}HcCV(SC0Ed!UAfQa8b|W2ZOm_0?7x3u+1Hz2 zeu)N)j_*DsJ1!c0gZ%Px!$@+EexRglS%7~*7Tv8KwTGbqoUp`)to8FoLM%XoQSRtX zu!^rSe1LKzaA5Om(Cx`$W%|!4p6VhmVejfVJm|e(e>-{~F%&K!b5=e>UDQHuG$x~H%5lF_G$RX5h3trH)&YM{ z%0T9iq>jiPsKk3xV%2jo7Pg98nkNAZ!&5*`&Pf%90g}o6`8h?Ph!A1e7xNni&~z4n zq6Tscnkb4TrRv}T&(Wy346at3{$eSWCSg0RnFU!URW!*3%2*qcvYq+l+EeYwTqIwu z(1fn%HZe4D)B$il+!MKtFw%|!tyF)1j2*Jo!kVa z3&!+m0D><^36(heA6DqzdIRvrMPITE(eq2g=$K}42OjK}6qOA`n!W}&;aW+5hw)!l zhdAtI!7&&Y+TxAZ{+aj~DDZ-xP+{OzFbwB7(-%0be96m;KR7v9AA2MPUz_2~k(4hg#5n~LDptYHi-P>Lw*6^zh zp3DqEwq|U)c{NoEpFVDyWdWpNw!QIBb1OvKH5Q$toZYv`3L7 zQvejGK<#rO)Lbor;c*qWO*sHT` z!7)de|9DsQndu`-L&mdR?OxErBVA!dEyK^3C2kZo287*ow$Aya36dr}W+VyVMwnSi z4k5+_H}PoJ4c)@TsA_%`anH*V5-sC;^aXtJep@zM1rPWL6TlUSV64-fRhM))4ufhJ~Hz zPPI#ELAqlQE?CVK#ZE2qEmrBdBOKw2)wa~cYgw>I7uf)|iMP#GRD{Z3B3i;fM+0^E zpj-oZMI6<_qKF9b{CYLp&`P0)j2eKI!|&lwiHKH+2FTX7(SNo!0*Xn4U6KpWo>PTz zJSMS&ov@{Ce4qE3VRPU+QiQVxiSG+}t*#ppAAloy2B>Vl_6(5^sM^9Fm2%s=?~D7n z2wS#4;zW~^;PFy8*8_w{Pr8WVi2)ZpCItw;li!)2t@X_LQ+R4kXr|pv;$wNFNmwTN zvq?~~P>L?d?0+pj6~QgM-`*|da&(#m zGA}ZP*`KPdhk9IrA=``642y&&#ThpImQ@8xh=?w_0=N5HVplv=vHFQj7t9$vEPx2_Y#mgbAR@?- zcYnbB^KXJjBn>X7XghzTNFd!Rs&5-6+Mds|Y<~f5gLG9a@OTMKP?0l?-|DD) z_pauw&nN^%drkEN4#xjT#QT5T6Cr`1 z7R(FZk**ze^^rUj@G&n~xkd}XB-ePXZU+Pf0j^&_!QJ`dTlr?O zv0nUUS!rCM|8>nbXgA53Tr10zwARW{Mih}OmBDiUJz1@OH_Rx$h- z!qf+J46q6*hz74jQk9?G?(ePFxd;k>yLLw6n%U3;ikiF#@~6NxpqJBNia9g56EZET z1ww6MRzD@SCbLR{$t~C}M9x8sVN$n>Up@iO1r&0I_L0xT+F{W5qxoueNx`72k~Ey> z>lv2FrMEab%?M;)06XHe5ky}$#8mijULhQCN{9RYMexXfN=KchmB)0BJ}*>%?$!n= zLFTVaSdn#Z=-rIy<)H&oiLRdS$blvfHLvH&oLFGS9Tlgm8gXoU>=;zJu#DDZP}^zn z%~E78(#SN&L$9bb5>jF+jl#-}g?z9eE2&1f?!&2M&dPPfvDqGVqD-qR3Jih!_OXac z4*|Jlf7+eM56m1}+@@;EfX7FFuzt&a}}PT~g+#R^ufuDYAAiY*F(mud__IkS>* zbsvY$$n^xXZBRJs*1)+r>QWfQh!hmMev<2>VKUD~9#kSpNT0^B5J3Y08_Sz zyd8}2@lC!44n}<_KNlXx+RR>2st^pPMQBl*oW%-sE87d85`0k_u7Y!ah#(8%0MB{B ztF*vHrFKMOgJmtS%qJG(KExjh+U4np z8uf2)DaB*u+o?)Omhne*AK1y!8t0x<&#>wB=VWb~$$28{+--!fSR=Pgo2IuV(hX~J zPS!MSxAiV4=H(JF^C3BjSsN_RC=Vi1ay2WrT6+(y4ElQz#8}OL*58Jehpo$$wwwHi zVrzvoQUXP0%yI!P{hAfw5h!?)$BT744IT^M#14E2=Yx_U%! zdN-$Cc7gJ-o#U+w5N+hCg3_9Ui9ZG~RJehEE`>{HD1A%*rqABb1;wM_7plh&yNU8GUP+B=+mOIKMwWR~s?4%TcU+%QY8BNm!E%9bOxI``kgK+6o zbl(~Ro@Z1zJAc>82V`fUdy*^5A!X^&RG+eZ!Cf*})(@6N_8oQ2u~lw2sQ{uJm^#hF zS?h9dqcW8VH2nYmo3M9NN(}?LGNGNF%B~e*AEL^usl1DSoJ90}UUECv9WEpqImj?M zTVS?3!JcXVb{DTbdGO3BpjlNq{KctoPLG<)5QSX3J_LJFN6DdsDNTkUMw2o^0CO`- z*vP>KmFdls8)IjkjVPpOiy8jlBMn;&EkaglX1N<12eZjwEpeAvm{O$;FQlLsTAV~& zt4bck_gP4Puv6+iF(fNPJQEY6NH}9|=={AldMAkp=jO+!fc8EI8-UvU0wv5$A+Rd}^+ZwLj|jW}yrx@Skyp(M;%PD~-POze2tt0C446 zxdpzLDDQ94Z;KA!4dO%LB=}?X!+)%j<3$5QGct0QzQF+&6fA;j42dajb9NuhTeAj= zztu9;C70;I0UZX;kdQ8mdY22r0T~JUVJPvV;ADh5mo35p8v%}&Nx}g%f9pGNGZ;Rx z0((7hl>jX;kfQLcl@*WOz(ix0Sqos9qvHr9*g>WUg6=C#Y@8&BjSv#83EE51E7Q3i zI5~AYtwko{YhH1)3Q8@KPhE2Z9V@Zlm|o#0G>ASh5>#M`syVBVmJggVCJl7Z*Ga1Q z%n-3wv`L9hcouKuUp(*jf4G)M2aT+Xu&aJ3c1-wI8{1<=hd4GA92{`=Ma_SmZRE+e z$nWOtC{;lFX?LPThbmT=n2`~cy2#1F_!L9T^m>#vo1zVM%*8Q)hIHOi#_jcjnN(K{ zq2T0mM0P^C5_RfC_bbXU1kGa-);i2bLO9QtPM*j6()?!*+z$nhe>|Sr{1~*h{6?63 zPGHD2q=E84nzJH(yKx3;zWH46J7+EfrD`DWMR5-!C@j6Ctl6K*(BU$&6L|s>WhZPg z;3SfUBu>d9)hI#k$NWNX3U%_uHZCC3UoC|MPAxiVB%S>!dUZu{pN-Eoi@5@Lye`#lX+FfrbI8sGB0M1GYzueU{6W2FWN%BzNN@5;|RIeMk^9-Ml zw}I41!-o#Q+BYeArP5)l^4KoWwKM+S{~^cFma-7JEPu!+y}N&k2jM6?!@p@1!`~!J zM}sqOQcL`OSWe5ke~RKX8=c|bEDh;zIvR<;5&z9Hah>qre*ynZ#`s218W^u8qj(&NYw=rvV(*-v+JG904 zEY7U=kTFrtLRp83hdqF$J|k94j4O#^UcLx6EM55oeIOgJyPNd&J?3F$-%XbTQfJt2_moX`x3 zf1!Xx=%_9P{X;5kC_J7<-)E9;cZP>v5n)> zYB#L)crd2FaT3MvV68OF(=fEwieV3Hf0-p-jpm)LWm_I_?sVvw4^rE_q6S5DBMJut zW^SQnP6@~1fNfzwciOXscTvFh>B*B#oPO#p8)sQaiX3Qmy!BE1;MB66vx|G0u{2;~ z=Ymud*{dg3$DOv~YLS#WQd_#nLYq(vtYho}aV{dltTLBdgw(P9_(ckp;M|rze^QIN zY3@2Yvr?a%Ha7LrP_i^J4nI3}bEKR2s9q&L1TxK-z&t=O)+bBnZJ!DINosFPXWQBU z?2t{};s=tQ87%7dtdsk#puye;Jh?cE#NT*`v_PkKU)5xH=UeUDQm?I^*+Aj1FDtxk zImwt#fk={tXP%_+Y6O**EmYv=f7QhC#`l?xjgqHVP>WDmrIp7`9FzDGhhu3UACk?> z&N6@OI{T8-AKj*#)c)88tEuq4#0$`~F)zNWY1AjkZ_`QaH2GaEc-1`mUBC1v+9$?C zls*AUBfuD?9_prZ?jK6^ed+m~8+KZLR!T@4?7G#-=Ip1_#KXVppsrWWMd1Jtf|BsUX&-76P(5!h%6-x8G!v{f91 zGa9xmYv4XIYaB3+B^4=xOXuW}p<(?{{l(>4b*L z2@_B01k0Kex}wk6Kz+Bi0@`T?BNiazOxSNil58rq7F?NkUzMP>m%7OT8yMEMnrlVa z8h#V`sYol{S`v7(`mU-b(Y2TD$pJ55I(;g&DZ3a&``az0DcTzFX+=sGKMZ$txr;W% z+^BmYel*s-Xd>vU8%I2oro4(m!%|U2^Q~V~>fw%j9y3`)Z_CD1`4jKkK?FA)G06F3g zWaE9C=w#iK{gqZlVE3g=@!@*dZuBCfnRD%segcDV0d4GWkf<%QD z`Xg(zJfnhZp3*GvAJ=8Mci!uN_O(&e+bPQ8GvWDsd~T5zOD~FJN4rZJlxMS?3;Z+{ zE2l%VxC{QSe^r-w;dh1}rmKtwX3-b?-7r+A4vcl_t@R>rv0`KoqoNSmeK-|J?3TK# zSRH1glSCPJ>c10}WWK;GL07Vf3?lT9B#9(U*renuCZi`|zfOs%YAVZgns^je{OV2R zW~?>`1HX^H!Y+v#=)yGems@G(-uXVuU9B}^+-X`oyTXih=@gcc1FH3mCx%oe%FMw3 z)t3>>0Tq7@lW@w;&_Hd>AjGH?n^7R%3mts5W@JnIA%R(c#xhq+0auD#Lx+;Gg`irY z#uOx#xa-=&jmABNYwYW4;Trq8TDZo(whPzj-`<)vm0PtnYP`3dGBh@ej-0uNip6lO zzf+f{*>U53+)%E@70X@S8~;W_9a7&=uBIAr)Y^ZPzi>@gk4c9P?7}v1)geyEDsS_$ z%NJIWn}=P#sFb>?3#_p`XqhdPYKJE2vRoCXPRpy45G#dq_H97xRv0rws}he#Byt>8 zV8;&ZcIlKfIfZ_4odeFLZBHI+AyTVN-?o*@n1@iVn&TrH%k{XxFzijjBj;*Z(*169 zUZa0_iq1RL`RQV>(}6U|C&CS2`f+F)iIl-CiEMMYGJnU8hM~gV zGczPz27>~)!&(h`|4Kyd5I4|LjcDWz=g}+EuBkMw#I2P#>V{77nOI3*kc4H+;E29( zJRTb>G#m^VTnn)q?5bb>nc0ZO^@Z?Q`|{F zTwqd~row|54$K(}LI?aAgydT&_;l}FFXwGI5`Cs8x#4{Qox?L4sj;gEA%#DhrsdJu zAP4=!~#eF1x27fRBeQ{ykOqjU{?C-?NxA(M*orUYJr>8&q>E4+b~d}GkeCJX&D`63%Q}8W`MA$5NMjpP1*8*Vu5Y70=tR@2FS-__Hf96OJwXGbXUiZ_9`;C*LZ64 z2)G<;D1F%e%m#U|TPSDDkT zrC3kXy0=+Zr}5afYtK%#lzF5FZ8%_L?9f3Ae(|HFG$cOTe}8xFwOUhB_2)fsbX*>i zE(IyM|Eic!%ksLVXKAftDf+Fluz@(w7KM(+;Du{#&C9Ak_}GZWwnBfIR@k*VyKkS5 zQdT$5nQeqJ&k`Po!{z|7_i`Rr&RkUUw4(n9X+?^)ihTNC4sE9dv1d(W_6SX|%7S)L zTbkI8g}I)JqPL|ycBnPl>FC=y@%nlYxu8*tkwlqc+^U`v?-n3v6*5IM9TD2b@gN&} zg4A_AQ~ps?&9upTs<3~+i2pEseeC{8qA*F%{MAkyTjy31apXaX+cn{T-K(Ison!hs zl#AnSqW%P>p;{OseoHmgw&WGwFaqzv&6?t?b!jj5KRMdgv8QZn*Mu6kKNzPTY~6vx z+wa(_CHwOB+LuLV>vmpU5!!=J2=BME>xmrw+(E%M0Ct9jpF%p8YJbY`tBI_5om(9@uHh=0* z{xnuMu8+*qj`hyY;?CWx0?GX27CSS`D(`)CU~%lx0acT^dw8>1wi$o6oJ&}%a|uD) z!GyIwn83f?63*sTrG~RFYt1Me*)^OSgQ-aUi) zR(Cud88xug&$L6|8gf{!>k_hK?+*&@c?w{GPwkW#c z7jFkZWBL!Sq|O93^7xTOU4L~G6|4Ik#R64t-Htc&tHe?Psj0|bomLXE`yCLQRe(LK zvUfO8W5(u+yuKlb-Dk3ycjfinzo7feKkQu0Qt;aGFIB2I%-DubX{X(2rmnQdh#e6p zS31X|s>1T^W7{u}!D(Pe`TAqJs*y9U@D=%e90M7)zz(zVD5;H>wtwp0+u0E#c&G#>AkHhcV|V8H~cv$_QLpf==krG{?jV$4cxuM`?q{(Vtnl`rSS-@rDu+ zY~t4SN^08l!lVVgal4K>lsM7WOYK&O9BY5t?1i z7hl>lD*eIc&Q-v54u3;`CK6>jiX#7@J9q9w@FYrDFCUGqG~zxwZl?{mV?RB_Xe{}q zU6McrX(Wkr&c?KwxE4FyFWKH_u&Qy7I%|erME^zS z9N4}aj#3jlZGCMIw#uuM2;IpBj!9QWo#+TQ8hqGW?|E;fox&DeAa#x85_>BdoyL+i z;6-AB&E3T^(n=Ye>U7sZY-iga>SF-dNow|(-Aa#^Hm|G$k)k6i_`C&{<8JR&+$;mH z54UoJ-U&)qM}NtLn+FsgVVD|^8%*$M4T+5Re6ey_XBTD#@v`@TcwO7CAZXfH8bk@5 zkuA`8!RW8cV^Z0cv$fI~ktMZystxAEEkIz~&KUfIo}CV>jrbfE$ysGlS|YEyW=zXi zXQJ4$M-o>B43NrOZCc$izvN@c)w zQ$S{6$K%mHq3^hPt#|YJC#$O~6cQ7WkYf;ANekkEO6(DOj<2iZW<#zqnZ$8aA2)kM z^%-9D@gH($RA{jSOj$k4q|L`6Q~P0p%ytME!0cM%tqT(1;&(uzQ6#hQ#98HgYaK>r z6w~2Im48MDAsN^7Mk7Fr-e#T(tX9}fgjH1+xTNR9kOiPD3e$t#C*J;!DskJ&*%>Oa zj4Tofd{k;%m%{<-bkTY>bPmPkM%$Bd*`7SBRrEZHr&Z5HE1uMqrw&>UHjMqH z4EMsAfnAIg@!h+9Dxp!*vL7c_rb06_p(GN?t}Lc@d2Ht>ui8svo5Y2o9k&?oF|(m6 zuYVaBVqKJX$>Vv2%>fZyT)|HT+zv8VZ(*;*wRP zNELw&U~<}#9EfSnvS^9;IDCsYo{$J94U@929|OVbqXwS^TEii#D%eva4@z>zkhWMDkZ%B`L)pUN{fF(`ms zi&hmnJ!sz=z4HF^)GECN={?F65%=*b*LuKb3Xbq&I@X6H{FmPGcGb0{#>e4#hhyfT z5qe!{Yjzf#{Bu?#b+ST-PubSED1YLmFrtpyNmH}d&M=9xbm)YC93mDd3%5&gom3#c z(;5=r&%b->JC-H}S`GPYQrF|q&5wT|iEmyPpAjKFQhbwpENm%4K92UZ5jEL@BBj$d z_3GL^Jvo9a>+2jc9;DvzT$NMQvJq7el_G_?1@P`u1+ocjafiCJmcXHylz(5UJrWxS zX%tp+RJwabk3QLZ(MPwTn#c@xK8xOMM*F&pl^%rS^*hYA#DO#vhQ8959>P`4QFk;> zM|y-F`^R2z%0wQ;MCu?CspI;ScpX^H?E!v2(l!u#^mkjHX1jJ9&1?v#7{m^72=@eY z=Ed9h<9%R>K}phKC=5|5i+}$iR{D6<=IZGpocXaL^*A~9j%g9YM5EK^+4%I09r)&p z??5KP-X_-X64=(6uL6TGlH+OJuw(FY!w{2@hM33>J`YvgOJohYQX0Bi-nd!tGxH%K zM;sbKbVpjNlIY;Q@3$VcX3>blKk$||+^EM;)P&47u^n3{+6OW2^MAzjyo`p7lu71w zwZcTlj%nVB4v$7AZ__myq9Mnk4&}M`ftAVd;3aQHsHC=pwW6~<=5 zFv?ZJ`77<*cP*z8_RJ(l#RAT;o0blgC&+u=rCKg=wVS@ia)R4k8(_MW$5b`ES69Iz ze=}R`7Bac#CV#&!=IS*3MX~+4D3){>o-)G&S0Cf=J6%sQczwi8=L1F+FKM)p8RD_` z#o`^FrB_f9&>*m{ZN#hqC#(a`VYsVBSrdmPFv8yp7P5}17dJ4)fe8Y--VaB*A&OD3g10#-O2DxiEI^z~qWogXeQt3}tP!Kg9c`a+2p%E9HBTBY$v0BZG zeCec0s-yy!sA46Xal4Ui^?Ofdi`h0<&z5CPM{R*tT|44Ewx)tg-hNsrQP+M%aq3Nh z3t&LLb8%`B=>aPwl0~p;A1BBn)C&D=x8!xcEPt+NRC)j&Eh-*+C)=Y+7Ur|!0B6lO z<^-cWsJVXc-ewA{ZeDEiCFYG1UATq~49-d2ProVz4r0}sI{eXkRaB9hEU>Pww!s{c zrP|a1A$n%hUtli4nx+p(r(iX`T41q_>CJRrR1xm@uxd|Vm$OZE+js4)BY6;AA=~c@ zj(@&aH%XRouAxyZj^2te=SDuSx>5rui{i(_2GG**WpT0EF~WOzN7sdOa<1y6nz0Y` zPSlPa`d#5EVxaO2ld<``3@Ph3|uV(`kX+L~^W80j2*% zEaxKXCVRf;*KiiKExhQ~!pr&=UUqAN*MikyvGKes(X_K(RNHv5ZyPVGZM@vKjb%re z(7kxGEtZ)1wG2MO!Z2r8%CIcfIj;4`owCg1=1W964vzJ~P^phN`QperTWR`pYYQNM y{IN)M`<#;6iVU~n;_qjF{hcEuyxuP6fBFAE&k)t$a|r;cNfrSOkEdH`m&3x*|5d)n6q}L8T`-GTp>m^G} z@#K_>90odeT`1r}Yn0?Ii7^6-kZ90*U%0%q>Py`Q!_JMG<9e+c7NZ}O4&29fyYDqy*hlnA3}x-Wpksy=gv(%4ugl-ChX{NWJQxF}>={|(N?As8*IN^E9TPeVV7-RYo6 z_q3$CjkSwlUe#yge|d5y9R(Mj^?FUUkMyQPOoes7MjO#u3FV*VO=j`c%NM^M+y%@Z z4KTkk!~BL|ezSJ_6dR#ZOH7Oad=(~u8F{XW!TC=)}naQrmbkXt>C%~i z?3ZPut%QI?+3$Hwa*}kKopk(imTpdVjHxYSs<8!Q1IGHM>ZaPJ%GtWvn%R1WT8(VA zhAF1|SK9tte`Pibhj0I_ufF}41CMS0Umdz~z|r>qm0>Ff8EpTp+t~iIoS=w8QjdYb zLeFCJ@Q%1b!vXTze1N>B1LSp?1H>>OP^Fp4+kQM{p1ys3YEMzK?drkZ@G z=zMkf%7Mprimwh`IpAog_{y-AgA8_x&SIHW6*Gtaf2|s>bj*swhF#J>AXmr^eJs(i zbvJ*%90Ws8_~1l*oCSlqa7y?Xe|tj~rt5S(4|)2-G1`v~>2me9j7tE%`njUzAaABt z20Y9r8HK;Dd!@eLL$cidhK6=yIdCe z+{9aApZTY^e)BG==Y_8A(V_kD_KmbCPL6=-g-0-@;L#BGRCTIf;>ri9#{~)yS>z_4 zO%neJgahf@#zljWSclwY9MFt69+PiaVOj`Se|3jwSc)d}?3bOnKo%er-4h6H z33cL*$8|JuMT6+XIdtfuZ9QIyt4`|@`+MfXZiBT{u&p!=c$s)n>_VK7H`&*Ji$&|Y$0IEZtz&7= z8nq0$v^H$rG8nt_r{~tA=bz*95iXFPdtuX^I)t?>w2X%}f@&y76m z()DBNh#4Kr$XR6hh|x2OqGc_LkmABJe>t8+~=l_c98;^x!?A7l&6or;-Y36k`NGxnb!LDWqy5B13jU zp_uHB;XE@?Ev1`j){5pJ=EcEGrC`?1jp;(1IQgaiZWXWiRZTz}}H%q`gv8PJ)9eN5n-fTDV$>4S(jS{4~ov7QaZS)f7BiJV4iO@ab4^^7gU}472RU<)GOaA^T z4uFTPCC0(*hzI^SbQmyMo9uEdUi@q=Ds>Tu<^fpydw+is>QRD11-?bQEcC5$vgV zSqfxFjb4E>EOL<{y#8qD9bh=IzR{?f%Z`e>U>Ac^Bn`uUey0br!S%o4Akbp$98jVX zV!$A}s6Fd^$AZG8fB6Qc7-O5&a|PX|vypj+$=~4?@fgPA*_2;S@E9;^UCd?tuS*?Y zUEcVq)!K4Tw-gq^+1h%KJ_8z7;)X-<6QH;cP@D=}&#A{EpB`~37YHAZI&h}%pgaEN z4v9BA+{wTu`ne%jkjSRA$d5ytM2t=BkUZUQqw^^8k%BT1fBqRcE$4j;+p*9~g!6tA zGn(XDYMX&1BpPZ99r?!;l%|fN8MOr+V~CYD55ckxJPvagfu#^hJ3@qF@mLxREyx53 zR}+NG4in(Wc~2Xin4td!<~srTECLEWv5uUr)_XQ*)^^+?NJ&&`Z}SiUh(n7z+uWiE zFxxHQj@nJ;e_VkaDlx&SzS#y`V~RyCUN*O&85W{I=hfrMw^IcKr(qlalAM089 zQHYOJlRLfOY@)&?=J*q`zJJN>%8OE`#A`45x<^FuNj42q@ckCtfowSrK)f^T_>_KQ zsghbLD^fC0%FC0i@f#{&zblVX4iA&ULVB<$9V|E(e+BPgF+QZzC>9)~sRakwN(Bcg z@Ehe6_>CI1I|Y$SI>NDE;-5E8y!JxRorOIYx$kU&e7L>2HE!X7)ZA{f=x1#f{j9~K zpS8wynP2Ax>P?JghOy5u@|lyw#k!u8NOGC&NEg=*i)jWu6bPjWU2^hz0^%P&mDXbEFQ`& z%0&daA{iyZPfB_%7GF#S8BejIeq+Hh7M~#5T{x4epCD{b zvL^_omy;OywBEVI@C5t$Mwb-@{7>m+D$zO~f1Vdn;n$SAO*s7yh3^ck!MhZkWmNZB zLd|6Ai{l{lhJGBGL~52(d;`sE{}p6ub;cRk%dQgicro@LoQ&M*y9l3_-FLZ{jZqxW zqVC4VxgU?_r|Yn-Z}^kY9r@!8HLOhi(mS2|00}Ud9`JAd(=jwtU>>B$K5XE6C&;@dpP4SlSF@21^brxtvOQYA$P(7C z?)YrX{J25yfPE+lU6av@g?q*fgQ_KHf7mZMZh`c{$PMFYJqXVYfarzT_#;H`5Fz1e zZc8y`hBZM&YnC5l(a6V>o~8|ooOy$IdAT$hFI)HBCW}R+g)F(*f2DgMb^=w?}8kDTg{~f@;tPm&6#?8t>!2WiQ(4S@f& z`2^@=9oXVWV-J5mym~UMtZERfR@bR~MVDN;%|DMYlQ}U|r%OJn!#>2;G@vkGaJXG+ z;3WY&0k%m195^b;7C@&Be@cZhH?pUY?h8*S=IQm~ya1c*8H0Xi@hpZEq~|*d3~_B6 zufO00F2N0_(GtgBEPq+o3fDkQ@n33wUDN`oT1{)mX^&aF6byFA!lm#)D*SHOp`*C9 zMR8XaSi2P(yEV61L*hb#hg*qR^c8}F0|78lq!ue?fm@1?HfeNkf868PhDK)i3gPFQ zoSEZCvp|swis)ads2*Rs$$c*&r zKcLrPP#l%uh8D(I@*vtD~8CbTTF$ZH#6Hbr2{E(mJ+vg)<^w@<47< zAAW})=+RAhMSl#te*|DVr1xQ>Nvw56EsM>oqwHLrWtyao{{yRqrQy#Ra3ytAlR)Y= z|GealZv$ZoBb3g=gT~<_`@tG}_><+%wCIq04#c&uChDd%*0A&LveDpAJEtY>y%Zg= z4}{30uz$Itt&n9!*G$yyI!t_99H;pNj-N$gnty|3YLt0Df0}|fKPVJw)w5Xm(_R#= znJ+LVz-kPKjGa;TZPquEg>#tOZzU2laE#(G%laV;|B!{hb6MCRm}YZR+G5%+OuAwb z{Wf0?=5_yKw^cG8F%J;DtO9sZAR=lut<2rXsE>I$_DWK4H;WAC*eRzz8{(Qx%xrHV&NJNDj6g<|DfV^@BW}-@q%wd zVM`C{FGc)d7+pX2AbTt#6SLmSpJ=Jwa0w;s*=4>0f7Trgycz5>>!<#-X5qi<=N~gV zST?XTsjf?Ou(Iu&>0beP%j#Zb&pCS6W9BaAj6A$tPwfg&Eva%%jiZ#hRRUFx5715- zral5x2xJAcs?_U513voJy6Z> zMtDf+I-}T|;qslj7ydcfh^5@5VS%xamTTmLDq;2YsguPNu!5O4#dKzKT(ee*<}hWo zDuoD^fK|Vr7waA5PjLTe%p@_wpY5 zt|JTy00fvb#@1Ry_V|9W&Oxse>~%Ic2i>c*c)tj4oyLzJ9V!Dwum-wM*|G4M%MOkO zlUiIf*amqB))Yd45Fh~CI8SYa6B>lB+Z|e%2zt}n98m!>WF?fPG6!jU3(xHE{c+)xH(d@}@(T_2!@7J$bb^;gH|j8h5rF z3;uh*ZNa$<|HP<%+#WkyWL$*4{iFlZ@ z{Xap6!HiJg&v*^OA#)gf>PG&+5`pa3RcR3aQ!jm_Slb`F({uMc zxvbZ(cuvEt_f4+qfKea_voIKV-f%tp5JhtD=7FAFS{nfJe3*K2e=0c*b2YSo&8g8c z*El{jl;=yM+#DQ}cn|Y%=1=`-q>T2|jHCTCz@CL7J855VwrK9682>#*FVAS<5m-!* zXyG%T09%K|Is8LTM8!m6c(94X(_o*VWtn0z1OaEtfcYSUjq*M$9W zMcfUqsi`UJ(a?{B@LIOnr5*#N4Wb?xY?^XRot^;349Rpve;LwG=n0@915I{O-@Ebh z#gjraDZB)si7YmfDsx|~(llOc+Zv&9Gh?#{<74+Cv6bY0ZxU^G%6e0_xP<*?8j6`0 z4u~)tywiUg9*Et*`;11i-=7mT_rm+lyCf*_Nngsz0pu=LYiCz~CNt%LHT2Fr;I{^* z`^F?Qy4j|re>C(Jwx;JvZ@05SDGip~pDbpy7y-h^V>`=SDF`XSVx^5CdF#TO#b7Oo z$ifw%d(+3;$kPC=Lnx%Reh(^hIRk_nO&=q9_Fq1+vz|#GQ2~oWeM-(l(j{-d&=}J& z6lq^{heMiCBUXXC_DL|sqe!(AFB}gxe)#K2A1AfXf8Pmxo9O~53`08}1(z0NS;0Jp z25ve@cQOEcd)pttkrKKBg>q3NUZXw>3C?dbE326bc;96nIxEEnQ8akzzk;`#tWkT> zBY!y%s3yr;Bt zq6Hy+f2>QVZD)2jKFCY&lsB$yth13G!bFF{E@jgFbqSP>bQua&YZQInLDMR?K}!9F{DzIW2jd^Iwr*ZwP^Ms;)@-$c z#cIIi|EPp)T1@;g<8VG5y3<%RAN5(xWIGFESw(NQ&3qO82>peG)=9&XwX~(RP_L*% zFbjiI?-qxmx0gQ*Dt8mWPg%rP4eat@svD4Cz(!k04R5-0_n=U<8hMXUb`0K6=jlv5ODl`eSc;9*>qf zcb-y}Z^a4nQ^s`8qy|;$kV(}kIAPK#`>XMSH&dW_*HfH5`sR{D0lj$32@hTwp~3s1 zWgYjYr*h+qmLiwmX!6jY;yU2RrMi^r4;<&p*(AdGYXRd0A49bZHrqyL`EU7|ERWeBTnJe{qzhHf>|g zg8kc!&;Rku)+H7q2~o^x{hcmG*-!ZeDXQczN+#MZyDG8S>zO}bRiJ5r0+@oO7Uhxq z!D7|E)or!pn@lDT+Q7=0J7`skOdWKj{k^zs8oc0`mm-+Q_`c#>S~o1D7cs2zeQ#Ef!iNH)V8eajpOiqS6?9N0-yr#uBWyetZ`9BlR_| zVc;i^PpsoZYv6{%oH0v^xC7|s(dlCFULHThr@`;a!IyUk!pxv3f9y>$lv-p}FuL3> zzA%iunTtn6^^UhS^a!2~H))`WvZ_feTda~K3cHQ%sKlM{x3PqtOx)=(TFMk!tx{wQ z&C1E%(Kf9;3NF%gopHV1pN4cp5}re-aFt{1OSV`G(M`4IAM5 z56iYNd$Atq`cDh>e@A1t_rNgK?h?-o%@E!3V?)y;wD0oCp#tc2UL58T!98ju}cY z&4M9Yr+}AFgu4*~5hR)A=s;=gE0F@aQ9ysR&@cChfT13je+drE-h0dHz#sKfv-dOz z-bL2fe@BiVs5{x0bruE_%bf=C2rsGeF;&fq0u&eV{5<#2EyWZLRUN>u6cqN-pALh| zb&TyFT|n zgU(!f_J&>4f0I$j0A{(SMd|7X)AImUS8v7HJ)2M2D^eLdY0+ChM$))T%XX`Lzf9llJy2L}dpj0|u)8Ym-zs5Pm zk-20Dt=3XuOsZexbYuguz_b`=anBulbn;Y;xOzB`u3+N?BpI`L#5!1Zyg6;lCEqEb z7l&8SK((uQZS@pb8CX;I;?xZ*)lW#AFuBUvVb|hbmN?b{Eq+`9qJ;@0+6{X^kU?1S z*08z=e|5XcLqe@;OMF!gOSSsomw?>Dz+R9EO{K5@)ztc5wd4lyr){9%)s59%KEh*f zIy_+9eq|MJQdeuM?ifUa-9POI8@elHdpEovuQMBuy0cs5L&^gNom#Cf0K{Ia_-noR zkMQjp;%v<>2bjIas$^X~Z!kz};o90RO!7ROe_8~ldal~(Y33x30lgB6RN!3ed27i& zq(Wws$AFp*kl7jPQR9StA^{?oqWKm~4hNFSw%FETrgC(Y(uGWN?-WJpE){5UO5j)< z_dPzL_wE@+HxOm*nztSUacS$BFJ%wik7p4_+q73B2Uy``(Z~;2vAn_ZY-@;}@mTQK ze?iv*qVaSbFo5#LpMYy$!G=b=ykX?jk!L!3s^;U!j20tF-8p6?0p+9TFrPwXfs#lf z^0+ph)CRp;T>S)vHt4X~bE#aKL6$hfd3nwXL8==MMpTwX zc*&lNY@BSq?sx>H3m3BtkHRnj$ZG6|1%$F@Td+LVfPU0zHsq%VZ^RIB2r-`de*m_b z!#fFLcihE84x3+(@pIQl+Eo+_pVbfO-N@fW-Y{ur4qvsT8Jm9JpLlqZ;LXu$W0geN zRS;!Ytr5r%z11D~#rV}#Y$6y8<};EH-~fhWk}5wCFzEUJgwyWXJA0+)t+T-clT1CH zh7Zi}ih@;b{qe|wDqrj~^ubBHe_~**pdTa01x!c$WIFUdJUgpog#}iGu`JCh!%ZCW zzznNUCz|I@={pOgf8;6dUy_?m%ht%wY9Ur0tRi%&h8e&KTk1;I6mE%p2wHa7>iJgT|QuDTgw|>y?m4W&2ZvDOu$~%(_-*_JRIuu)l_N!MP~fJ#@s{ zt5sys5oCR0VQYv+b~TNlgqn#K3+b@86de}f##&5=;03hDMIt+weD02V+MIgY1=N#2!0QrFTJu7}S?YvQG$k1KSIyOB{^ZzC zX*519)c$0ZEJ-KdPC>Mole(7fr?gN^*Jhl5#0G_0r55*07^O11OX#c`QaQ0#ae6)Z zu^8QdG#Kr5E1ntmfB%?T90!Xh&sz^Y(gI^OyC%S>=1v%ouvP8e4qc?#Ri+Jl&Mzi?^4B+g6T>zfn$= zot1-)%5hEnf7P@Vu!{)9?s;;PD6z?3hw4eafYeO-1+D{u{i9Z}jI}&n6~WSkLc+W$ zD0+#nd!tcICne`?d2%XwHkVd888O6t?LGYkIG}MzveHzPjrgz;QxlG{%9f=97PlVNM8EvnI&Dlr+G}Q_+kYs&LfQ!t#~SoRaGJDjN#txq=jZa5#oX z$In}bXiqnSb~$^4lZPe?eYA+6rG&EK;jbsn$L4g{qCKi&Uz5^=yeuBd(H33s$MF=2dsu>i5^#&FHge;E!Ri0baZcthPx0#r3C^ z>TbBsx2cK?x$?tbAHu=zPiaM92E}h-%ogmO>#%pepL^566;F$iGe^5E=mWMLe|&`+ z%oTNS^%Ld?GuUWr>Q&8?sUP=geG-Q!)-{}%AYfwHRF{;^KVHf{4opYBP2zk+DRT&!1Ek4nkYFu07o zurcnStiqW)%q8!;oe<7HJhpbL6 zh6$|Jl$plqt8%VD*!7cPA8J+jF{v4*6Z0cH0u&atgsDCm+|^hQq~9vh1M=?WTjw7W z_k*6m?c6~k$JG+!dWJPlThL_$=!X3^*L1#@r^s6;< z*9)p?y;r{sT(OWj7( z7`fpA9RC&nkMM1^<}`Xe`~?g3h{g5jvoIKa7E8WD)Vv0pl+>L58U8u7Yc~8-l@?BY zP6|B86t7mT(I0j~e@4Ii@u*@4@2r!qUH$wyoAoLu{qc*O3~w+Bx{n&xW4!W%66zdq z0OFolj@4Z;lrNR&tZ|MJZdWWp zWmK5U2rPljisQAb0J6P*aPs6okJP~@j0J#t@1BXPz;G|Jyvi6r4U~Idym?VR#85LRH)C-Sp|MyOzpxYbfaHLw@R*2P!SNg&D@bSkWEzGtInBb(_9b;` z!ZG$KNgfJ}e}HrwC0um~RHX@vC+;~D6@TXUt4v)Ss_NqK5C84Dj6E7Boxp)gZ^)k0 zm234=MHK)NE9pJ2yQ1V&_lV<2(Fet*k|d#J<`CI62}(z^#~l~q>!2!}Sj$4DHO6ry zTFWBpDQr-**{T-gj74HGoPZlZfC0^1NtDUhQ_J^Hf8%LC{t%Z?&`k9xP;lVrB!aDC zh)VPuQmw5tO!+KUX;K2y#KV_9Q@Kep0YRRsP{=VJttqFB$mKMmy^&j3T6yo6Ei1xd z_UCkt^v^CcAT;!X`~{4mSQZ`s3eIt6YAX&y1^?zOF1HzaM8XMYbb-LGHG5B&nmcFV z+;M`Lg?+v~4T_~}Z1FQWQ z4)+6Ff}(gz)rp|m)mg<~Pb|)o!Pess>11G9e>%E??Vkn#2+6TGi+Y_JzcW%36*UVN zy;!bTy~I3bs;%jpxKk6-oOZ{1ghp;*FrN-9PNVU2wwaAIjH>*eIl0LOR^1@IU7LzVEaU~_DeTJ$5m*k zC(K2}vko7|I8z_a))Xy3|AU;!K0UD3e{Y{&*;?VN!osih!XFC@zmbJuJMKplSQH~4 zZ%i+W-cId*AYqXl1R`$>a*zkO41tYggO|wItAHf?LS)ypt)HUE7v0DqxpKE{!ev9I z_}SR|VDpohB(<}%{K7oyFGxlvW}&#Sdm6}-rnn?J8+%SZ4hrrEKYC%%|8VuOf4|`V zmz673>Z(+!g*{4=oFtTkV=GbRYWXT3OI4|MC5^R*=mKEiFQ7HLGCSl&f6(0zy1Opj zi9Hc1EQjWf)gnvMTB)7)2W|eKO%rV*R_os66xb5rRm{Z6{Y}_RYr!0029dC6SwN@> zY-Q*!DP2rON~`$#IP^h zs@4v3H%2dO`|RDwx;q$nv$$tl>*qE2ZxFD5haUU|SsebI4hHb=FZkcVGxqP{Blvsr z>IDA%?V!xkQTC6bwUaJ(RamTfABo8YR&Z|#e)H1qR?rJxiiKoEQzFV^f6iK{wInoL zhoxd9t=0fRVBfA`1uO+Z7bIBw4ZFr%bz>{smYmdfxzM=_p;J&Q&=N7cM9pkz7Kyoj z9{NLlM{pCQ^1^vtRcq}uCX$e9WgDrLK;Xj+0v|T)Tuhyo#8kD`NytiresHVMALN5N zC<|(LS@4ows^Bb}|Qz%5w4{cOv;mtNnULORL4K}Th`AqZAvgG;Q^G}Sl3n}^sE&mm0 z`MajCcmCuwbc^@Rf3q?_i0ucl-9#A7A+ldEYf0Mrn+j6P$+=63f2Ox86n_JOZ_zSR zik0GT?}%lsEhUXEx1v)US?Oh+Q}1K>dR2VvYrmYnn+BKD^17R=u&n296kP`4&`v#WV7X78AE7x#`lW-&w9`)>SjI1~8C-&3 zm3{!ha)0yUi5tDMf3@cjJ^Oe6vA-dE<4+?%{(qi8uRM~V3=MYW$pmF0beU%h&6-nA zQqnLB!jk+-W0(IjVHdxCKQtgV`7mLYzD$^Q6f@w17a7ruA-F!<10ZCDKpkPZJd8;#Wm_s`iGjE^yUtMWLmbQ(1*P_?3gJN7D0 z&8j<9D2|V;e=U4NJMOLiv_=10wT8Xbqb4>9lQG(AG~PSSHgwZ;npS;#JKo;5>hSxb z-hr$JGz7VIo*378sZkv()>%zTgqm0iYqE-%#}ZfCANaQpKRzBo z9gDu2n2(Urj}|7uKNlj!5oIu^#mjy?+F{kH!WfgRf4VH$VO2j`M_Wp%k;5CI23D<& z2lqG5(OK=q`2)LKG<|l>Qy$QwWjWfK5@VJz-P2y8d1>p1o|>JsiFOGupHx!T+Gvqs zkvW;^-QB%=$`c4Q*w`Nq4<^Hk(U_-Nv***HcjixFY%4wVNgte9sh#oOJyW4e@3cSn z(F8A|e_l;x?1#l&_EbRMs`XoFpL5_jamw=sbvaP{`2U?@snr2$)&0$ zDU+pxn`-9q@0n)QB&C_N%cKd#mX4qD-2Fx^b&9X_vKU=Z;`3(>KrGD}6WQCndwDq$ z4n{xyruuF+N&8ex85+&~;>DIwD%pu+UY=4Re-lsEaLdqHYx&M5?jQ`vNebE@$tc?N znuU){i4a*aw4la|sMY|vN`kQCV&Jt6Ah#ESOdu$RTsmxeNa~6l2R-}I6P18Ev|#PM zy*(3uqK)vW3jmLB*Fi1#f<>-4CRU#>bDs@ob9@*SVN3LE3Xb?;(4RsJ?8-&VdvB#e ze}(#f)JH!9J)HI8j#&e^azrc4gicugiA1KRl&vN3$|Q<8f~yU-3DsU&P3Mou=xgrh z2|wZ+1R^cA8uF!_S|0 z8u!C{_rm)-TYHtwPN&uC(bMs;=WJ~P$wB3weeWlGEnKsA@umb5u*omN8gBi+fj_O- zstIn&Fi*Y$lE)Z(t;$~upndy=T@~havG$hSLo~wjIAE^cYuXn(RI%;U3mwc5e-FAM zg(MXh?Rjr|(Dm$s?RIamd;l0+$pY~CvzLA1!zDox`i5Qu8H#z+_j7OV(F=gjpDQV_ z$x$>uTB;ekMTSCa(6WAMI@x|OxslU-P8FF4_3$t>A-z#c&kln%wFw8wnPDAMAdG6Y zmI7S9uSx-KO2jk-={$62qdw($f52KWJ9C!E9)neqhh8Vi292i6=;t#1hU?xvQTVwq z*!9xo=uKBr5Mw;OaLM;`&4507Ns3m+p7O?a99-&cKtfI31Erv`6k>e^kLW$V~Z< zxN{R_(~LuAR}5-1$9ND=`~L?CeIG9=cRE(o{`Dw91zs#6=O$WM!by3H(XEW zl({MA<%j6MWYlL~6mxape-+~t=b6$M&v|)Z)m!*d&?;KJ4CbFr|LtA9oZ*4OPd2B8 zZoS@lS9lVG}-;&$fxVVY@kZ za@ik2Q$ctIY+lWn(WhMmU8v}9o{=xq%RkjOuLzDCX%{y$LDteH|A1&UyR}5)hcj{9>=+OK)yqb%T1AO9>sfRPN zY)iaAOnxNUzjj0te+KfDT*31BbF99ZXA(#?)w%wOK3@iI3{8?BNDJLEu$4+K-B=g? z*;OU3p}VLWJ&P;VBIh#`CkW->(4MnV1I6{2b92jq}~)k%~!Gl6hTV z(3Vu7HEfD|)v39-BTajY-;8pyV-BM+;Drt~+YO^$$WK!Ae|ca70KJH2c4f*Y7@c*( z^ny4LWw^6++8;l?%_e^6X4B3>Ln85)*<=dcah}asH3}paHDj-uODA%PW&aF?s<`m| z>D;5io3FAze_nKRMTpXYzHdMdAD`@#oF7F!7uvsvYe=-G6aJ_}2mWH5d;K$%jFJv%6>C zKZC(A2jaceAVh!I=q~#Kisz>1Z-NlXjri$<`x{vQ0W3cpkVYuziy+%Y=nX~tuz+9A zW`bW{wzn@|vgbk~=DW50(?HdkPlwuxr*j{aL+>t`e~~C1p}TObYmB|d;sSeZJVMuJ z^9c7g^8qXNrgo!RcmkU%3pGtH$ha4oPYEI7tKnem4ZckG_}>$lj-I^+gT`^opfsLJ zmOlYm{#nWU8uxnz1+L$-un{*Cm9gd@1%17x=llh$?>O<{ zYbIADR`f>VNz}aE^SYdfr5ouOk5NqpP|Z`##YNyeR@~5<;lT;sTjZqE6FygQ+gj)^ z6@8m#-yw4jC*?b5GPE?GsD^rs)!`4x*dKf-e~pYKPsA7?=pwG4O2;QblJDnf-0ckc z2g;N~HH>Hs-7N6kBgW0+sM+X%#FZ4_u&{E}j`YUdvp1sQ21`|(g7GUKmX0Kn`^0`v zeLGjuE3D=jox4J}POp+!sEmMW%p1Ce)x&jil2J4!aVsH8m`ZE@1Av;dMXKs!z*JnS ze}ZR&C?KjbQ-Lv|hN(pb`9u^Mq2WerqPc1=(JgpNN!rAOoM4)O<|`S>S;-=APo<`A zx<-}Xm0s_PRDPJjt?!thcccGBv$pC79ss;zdkQXrIDM{vwgdh-;78|0e%wSyYYM&;( zyq1;;8@NB4jj#Ht;Q^m*W;qLnhlpy};%0!Iz#1H?uNI2Rb6dSOOZJ*uhH*-oX~vkajTS()fbgl|a&+=!J(8>F}}w7Cb8t*J>Cm&2BcDA;CU z#ZsOOY6X($D7Zx21A_OGXu={qTx(T?-}R?w0frHSBItmaDxk4ziKtkz8kzRiJP|g^ zu9d01yDAIHD9QJe+B~BCDmyd9f27%K?wY@5OXyLjUZ-b**2st+z@f5!WgRszvyxwjGhCK2M?U6`bm@0=ah?Rg zT9xboVz3_veO|O;BNl4#Z-$E2?CPq;`qSfKp^D9o?JA!q9uSKICanAEe}kYGK;mOz zVBj#$Bc;n47`fd}CjI7!a)(2}jp)yMU;+v$B25aYaP6&93~Y@oxIvMm1v0|y+O^zg zXL9GxdofgIYybS&u(ELk&5k#*yZDQIpV6pRtt75yxXve+QgC3w6a?G(OpcgN%E1Wr zM~etxLqj%VjR+xyIs>-ke-g5wLO)qgsoA*R9A;=n3$tB{cuLXc5=)VAgKv~`Hki~J z&8$s0=xDwcO^B3;EI1$F=otxiskeIUA*OPIw?ZZhC&<^Uh}xQ6wHk8se&-;){H~4o zyS9FKk4}q`H=f}-P;NI_Vzruq24MxM{EHfRyt2?IjEU3CMj=WIe=0T8k!)g)(WX_m z6UVA1R=ZNwT3`XNm4kda#yvX(-StflFP~F2k=g1JpxQFP@@M#ZuOv7a5R_r)fjg_p zC;SC9OG)M#ye%tF-Hv3QH9k!C?LONkw5TvE4Q@WCQ?x-S^vg_E;j(r9Q# zFDZk@#5Cn6GCvCyf4xbfXEN?P19v(NVqmH+JZrVSTC-Mvf<18rU+@$4Cplc7Kc|eoJYflx3u_$C7Z*Bn#~3-bN>-dV7?o$9rtnL~ zhLZOHtpYf@T`Ky)?P=;=^x_UuNMqMcJh{8*4Z6pyLPcw^f8N5YivNfEwiZg`(8m36 zFHZpLQiLGI#K6=Z3sZJhSxNR;-VtmNRbhfkGS~IhLv+LJnXX(d%dYMzfjY%aEL}1h z(|@9+ao3uvG`P&JQ-_sZwx%H!sR-w8-MQ~4g!m_Hn7dWK=PT~@F+8iq)&HsDh?`V# zNXjk#(@&Y=e?R@i0Ku#0WPZ6Q>J9NW_!DnwrKdD?A6sG~wr{%8HC^cPfIOdg>s`O5 z$jMzKdu+WM*GQzr*p0-kz+aU)$M3;p2!Fop8dQOMCMmv~k=U#3!ZNIWkwHIQlG&uz zA3%W7*E}_q9V0$2$h=WFsZqFW5K1HXjcsUy@@Py-e<`lI!Gg*l4chbD!2(;xYir70 zkY4Um3n;c{xx=i;0KUi|-l>GQx3W_4@+>nvvn7X~j7uzQ#*K&10@fDR{PoY+;PXl70npWSeZBucci%EHmz?ZnB00uJ??*|~`49;p%c zD6Sqhf8Dz8C+BI1>P45%&laAau)F_(`#BtvSe%a0HTuL*#)70nz98tSkP-i!qb$IG z38de1YgE**Gbw&-m(D*O6}xo$>1h>= zl+;6K@G8ObxpH8hVwKNn$RHTVprN}pEX-ytf0;2!RM)uc5;$)qamnL9ngYvS&p>Ru za+;L5!(w8LBtsiNv(w6*br$t!fk-0C-tA6nm|6!PX&L6Xnjj4^V0VoLE2)Aay-F(= zt6hJ8W!?iBiVeGDRY1hM{38dHw*r)1v8+|0hfg^$RWVRGvuTRgoSGnjDl#RenHPt- zf5**LufHkyJuVE_BYZJBwCpv*aA_<1HhY7Fvk0u4?)^Ef_mXMTx8Y=2i#w-S*?PKk zR-z5Zrgg$1`$)(2xY<0n*N{ePc8iR2oVR-uuB@io8gv)aJdEifAR>D#lhsC&B;2H?A(#ZHkJ7gk|`Qvrq!Hq0BS&$ zzdo<%0;l2f^VWCpU8a1=*}3IWsV}vjyH~m1CT`e=7m-4#4g5wYM73+!sa&y3UB6-a zfb)8a&!ftpdRmQZAWZFc;p*{=Ru6Ba9{J}}+qnGwrI){d%gf)t_VV|aUjC|Q4u8W7 za$qmXAeub4pfJ??^p8f@K90SaHJgtkuVzhrbo_$$TSM5Z|Kadle>nE6p*MD~u*i7q zCqSK?pe!YA8j$ev&(3~E0dSTVE{;wwRs z4g-4O@qxf%jMToG*jI2CTVn)R&w|c=b;F@43%dF>nBYA(39hnn(-a9pCGilK~4d!fUsR`YZWq>Hg_2BG`55EN?JaRu+5q}^X2_l+N zzb~F2VJ3|62qwXt38Q@KMqX%5$nmH;L(u3ei^ZQ~NNzw&uk{qD{zgv$)a>gifR2ZH z3ZUUYOL3?#96|`Eo`M(0p+1c{)VDE*U@+zoEXEvy$&}M1*!E$iBFzb6+<RiX~1xbjf7z zO8Zu$}J+Q;e;Y$ zs#Zl2dfLEnpxhf=Qf_?|PtXy0br;CX>iyLqvVCzD9Nu?(riucw>*4p3j?6<&r@|eyG6Xud za?zpt>{W1*aqw!mBimN{#E4mj6X({;?dxHgMMly7V!|TL;&Q@C@G`I>3v?N!S7OC{Gb-gKU-oEJFGt^pV*m!4Ochglj8(-)O$hO-~s zBXl*jNIYWM5m1HG)493~;#BsC^BI`DD1HR1*5{>BFT02ZBK%Fy%|+R`isy>G;TH@W zGDga1^nZhT;YboTuFOC*rmt`ue8|f`aQp*kaJ$iXYs)F*bf(WJcbX(4({opq+&ch; ztRSDsWA<=0;}}eLp!^BBTv=ZA$&w8KNhtkSaQ@IOU1h;CDMt^%g!fkr7JtHQE%O(`Y6|bQs0sheZiZf@^{|X|O4CaD{kgC1dS+_D zd1MbW62o$oN*~zOJp-Y1GijO+Z6oO|(tl3SVOJ||7*-`d8)bKMv&4t79)Utc(9(-3 zP8RSvRpxkER10I6CG08-rTav*LS+(1q$@s%|G!nxIHz$R-L2er8jZaQneL}MzIShc zE*V8rl}rJ;lb6{77ZqXx6GzU~FEz(&{p<-=s}KQb9A7koUZZwhSnKE(xpITr>VIrM zPXIh%m)^o`tmI7W8fSuU`ioFhS%9PJ3XEXEwY zWWAxRB)0q>Hoc~znuUp{knv)B>O!G6qA9LF;!u4tV9*2X)zlrM`bEd(8w|0Y;Lx+X|rKHZ{V4pZBT0uB!30joP5`4o*ZQnbF2JUuki*6uXS-csB*>E&jh(5t|W&p zPlf7z2Ti8&VDPJ-KjY5Q>-BtL)O_ESzY%x0i`9S$dBT1QRriS&Z*C;#0)KCKGhBWwh02c# zCa=8}>{U`|Sg=TIXo4;uXuh#)Zj~P2{NrD}Uv?Y}TVg#Xmm0L7RNfhmn=TZDQh!c= zaX1gn%0j|UgXw2dD>N)x}11W-jBhfcVM60T?Hq84TqR}o}M|14|_e5bW4f7(DITt;16wQKe&dNXM>1 zA~x`VxsTyph;zCddaykG3vbtogITvV`#|obG63$yX}?RT-+#7&V@jp-Y7A)U$)$1M zCjnd^E_eDuWm!J2C7d`F&m+#X@N=g6*NKB*9Q){7XE)=p5?Q9iDqh&UNEx^7_j~)8v)Zn3_DG*#SD)W`Q-^ zjV)`t(HJ`|$A4;dTJN_kXUB53);sVwroVM(XY4e0tU6?m+D+#@>}w5+f^4pL;<^Jr znvM0&xZY|v;b$94w;EQx-Eg2W$g=7UY^B-3(#|HixsMG zVtK5JHL1-u#>#B9w;;u#(pVV!$L`~1%K~@+z@3liOn(VSEj{p@CqBY1&cEZya2k#S zoj8s6FbNH-)q=XMvD3l7FgH$%hKqk%Exi9W}5h+ z1SjBeztAH`O=fFO1A-ZXaq4-q3N%>5)^mzd87%CfR!IV-U;x!l{b3l)`gj8=&T{5a z0z()A-cPUkwBc4fp~AJX0^oVd7&m$-ZPy)}o8I(PeY3aj4@pUmKUBAu)TS3zs4?uk zE`Ke@(3#9`m^hLNg;v}(!sY)FzRkiUfN>RITmlCFUFpU5?qNm7TF?tM?BDgL5sUUq z(N*;V%+60_f6*068Eh$WADQ^Tl{lWM%ecDdb+eF9V}TTy^dxUji~7ox zUZ%H4tTINGP!IJ|gMlr&EFhtjd_w_6n5b03y<5B{5KveGip*_+P>Aq517Pp?H)=?_ zzKS#7qyV~2Xa#O9hTH7T1_A@{seiCCI8ACwrruymk4Lc2{2VjAM=13knid#W1le{a z!Qb*13Ggg6mdnfQ?Z{-~a>#2HNwm}W{27>kl|J_>i($E~Nl0V7)A%PL8$kLeduM9C zw`qcycFZ)nDk@+@Ortb+k0g{*ZWg6#vJs_uts+XB7zMgxLTMo^cZJeshkv2e=`2EN z!-P_I`t0mXMW{P9j{&fWSy^N%sYYDpG>AOy5y)lhVK4^(o1MxBqWma_<#BP9MImgH<;B&!~?_kBWCu1*UvJY1)@Nkpj1+S-~wxq zWbj8hdTGI8Q7su~hvl&c=d2oe|5ss&;FFoOA0KvY`j|(jJ^Lkma4)tz_hA18mXNXT znI$fc4POH(-@BsXUIf>8GktG6sn}x86lbh zkphjIOYM3>6pPdCpqkc?hJIM{O<5?p?;5KR#?W|-S3~}s(zu$!Cz*$`7zRY#6inm8 zP6u+3NJ^gC!Qc~7r}{~sP?N`~bchD6g7jLK7;NbU$l{5;gwTA0HBQ14)zq|GW+M6^?&pF&uJ3u+1$>KrIH(S5i*61z87l(MVuBLxNe2y2D}VdNlBMwbjhd1yVvAJ`4XaNyGEJ+QTH{(q(mb=!Tv0*NUU3Nd zp?GL!v&N)#t@Fk08%CJdLsFUf#fA0PUu5&F^_5>3JvgDGIDdmDvn5*TmMknx$fO}_a< zmu%nfyJD}anD~HdO}@^8nbMx_0cQhjhKm2k_>tYfR5mfccaL+PDiCxxXXm~M_V#Rq zvF?szyKA#LK!0eCH{t(gdgkpWSE@^*qqky4(t>j{1%<+U;2g^Hv*y=iJmu^eUW<4l zx&_1AJUq-Rp0Fe{!h7%DR6Y7Xe>TI9rUH1!G4{o!AK{gS>p*VUQocv{;+%rpxT#1X zJa$hyc@T{2lVQDsagXZJ1b$2ddbWy^hx$ zuuW&Pi49=GrJExRv?62hDC;KIL!Y8EpRPwkWZn=8@w-dE>7v;+#tjisMKDkxLvl+X zxrJ5&p?^$oiu5KB7aQ;A&lp~Nx{eHN>`y#$g$>=IsFyh+-gZG{wtf*fMUSr50Co3Ud(PFN)HpLn= z-{gOY^!PVvkf|97t+1MKheLFkgr*W!o7+lU-+#6!vjFI|iMHQn7YPBT7vjV$u2<5;X zr~p7IF<1BW9Bl?M02MUL-|YwCmBm5>nN?B0(VHCk(yE{8(sbjK0t%*I4c??OjD5-% zZ=ciSQp<1>+Fi+gv2QrxG8C*{ovXq_)qiN`zlcyL@6cVAy_Q`lfU%=i)0~BNh+>%p z^Z==kLA;qZ2P86~c2O#o2;6I^r3l9vxf=s*_3RBMkL=Az9cPYqx@O;VX;ONU$G<05 zwz-a{%B)lOVo@|@^|7DFQNw2W_>igk#8X*Q-dQHRy|(Yl&CQ?+^thwuyx8K1UVn?) zp_{w2WW{tTwn>h5449cE+Qjxzd5BVHx1Y_&)cYo0RJpj_}KWi5kPF0btP?`oH*kO}OfjR6Y5B%2n04!m88 zP~2)*X+5$$V9X~E4i7bqdLHQ-%YPrTf_|4BV2g#YEQo?al^Y>@0i#w>8ihyNd zV6If!o*`|;koJF*I0X(y0>X-5&`|ITBp@$!SZq*KCU)9a8ntAnt#-XMVA>MX;yH*L zX7!TN%WzG~)jN9l>Y0rX)C>AF3svgQCUxmAc`{QvY6xXW+$7S+VxcC~jGEikYRY|4j`#vQ^sTY|_=?Jfs{TiAX06yfi>I0B z{iE65!?&^z?Jj<`w>w*#dlfW#Yqo*k3?Ab%%}NtPWc{0$*=lXIoo%Sciq* zWvXsVXdsxg?VB@*yKCI9OnXa;{CiW)gkA)Fn<}10(TRtP7gd5W*|FG(w2)khjL>wyAEan4TlP3UXEgR2Ws=<3 zK3WjbA`mwL0%%f}Y~9~}_xJ6pegF-CBIQ>OGqKp{uE(xjyQ=oPW{>6H;lWZbN{@tX1vdvj?o#E`Ge3L*- z`8y_hrGI3J1u5K$%<8Bm*Dj4zYwt$)o{U;Iqn}T8?2ZN>u6>@3H$S!&4)Y(KjXJzc zSHs#z^6+Lft@QDLfVHdRq#AYbay&X*JIM}^k6EVMWEdOxoVF0poTKuKlZ?Tx>qEe@8e z;q0*AfmW$n*WXIlUuo9ZtXX5&K0Ax3CjG6Oc#ooB(;&!O=3kht44lI!|L42^^Zoaq z92|fDFW)~MJo)tdUw(M}&z~Gs1>IvGK78=W|M|0Y*=PrUAJ4uy)g?Oo=HLH*`t$3e z7$2J#%hBbu{;|50)4wdt&-cUU-+uGr^gC}T|NCM1AMGFLWc1T?bsF;VT_MzQF+Lsq zr|0~?p7UScV2DnZLL{cj&{U-RENZ?ii%Q0HI{2@_5Wf*jx;p)C@RMwr2+)2f|Bd9o zSMuN4;D_OV#e6&}Pk)xVm=(u^ul#f{l#{4h$#dWjhHuXB6i3KmAgB;lRet8);C+)l zYgJUwG#+7EPOG=7rIeTT3=gZPtt0)|$m7I+9Um-W?pmmDD?gVk$ItB7+A4v!&+ta3 zv##5sxmCMTL5Mbr58KSSNn-^Zs_Y4sYs=06Q^FH}{bF^^u(i9g465b4l#^bXedJ8X zNH4)xR)$5J)GtpElHPpQ-}riP$Rrxm(@-SIQ%eODB}3W%pB9+U~2wd}h*GJ(*u}M7lU0G1UFhU>IsalyDTXVr(;EsUEs8%4n_s>G!k2iN4U{crgs0 zSn#BOEkP<)*1@2QHk?7j?zeLEs*q#5m!JAf3P5#^pyzdJ+GrW3@s>Y)4=RnQFZA*6 zKmgdQjp-2XYV~FcV}A$Ct4moHj}e#N1%Et$BV7;N?w85@qWCW}uz^D~1E1?_@$ly} z;RG>(d}&wkcfk)mFur?vUC6m6Ll+QVEsN2Ak0-4C*?R4;ul8t5?a$Y1M}4(lZK?fi zy>{GJ`>a*_y+Q8Awp298SnV47a2-|a$XM+f`*0mq>&RH`8vAe^RqM!D?Hc=V9aU}9 zIkUj=B+(>rhd=WNat`UaV?{Wai}$AIc=&KjiiM^_HV<9Mvd+t}v&)*cwUDtR$Wk4D zY#xC9rRjpW20%||x+81tYJPP#v7N!HCY`P2cAEd_o=ulBkO5o+&*mbd4hQ~GsHr&g zuN0FryO<4-sG@i~XzvsA8T-E&{Uke(%+)jQ7xi922cW+Q!%!VGgx#$n%^82S2M4+Q z-;?5(Uy3KE-Y>u4ko_B-j%Qb82`jFDJnj+gzqzUqioOIC+r&(Qfl>m)^y2EmAwZ6T z<6K;=Do1|4Dyla!XvcRhE+}XMI6Oaqtl9B8Fi5bp*@pu-i>+b67H=a!2X4{ut0O=M zZUN~X`X?9DS$p{Wy%hzdxJ&dWTa{0{8IY8pkzUXRf#{Aja@V{c`bGFJ47=})%50IlU>$mg!`ncmuAaB7Iqc&0 zmtT7J{lnw#P`u8(cgM1SYuGY>Pbaa;&!T(+dt`SsF1!tdg5SHf!4Z(f)4d0p~C zpDzc;eEipWIUXD{2uLw^IvviA4C+pcHvjhbtC^|vzvk!W>FY5V3)9ktef+E(EicS7 zMMv}eqAZHbwcgr2{B?$Zj}1bb<+JQ?lFz@LuTTT!aCeg>I z)z*KQw%&Yr=|drG^z?z(Au3W7)02AdyR-4)iyn-v#^0*KtamEL zq;6oV-O#rraBKsoOX*`cYsaUX%oKkSyu7gMqZKxOiqs`nvbDivI=Wi%0anmT9^Rj< zp^S@ldc=YZP&s0M(X}z_>gP9Ud3!mtpb_ESDt!3f`a`yqN0a#*Bd5(*)5iIZ*&Q~g z+GxB~nz1tB;Bl>g`>y)o@t|%BNnWfXG87?2POXOztJ^kvvAx~C@mQvASPiEf&%yL` z>!eM$iFieiqGmOIsW@eL{?KB$za(eW7UYkuEg~Op-C$9FiCE0}Cd=tP=x0nHKGfH$ z&we?ZSJT(Vye5p$E-AOy!~U=bxHfa802=xNli#*b&PL_*9U*)L>9SF19W$gszU*VhK4co0FTS-$?QNOk9|JNC1HNUhaIWEr0 z!J4+^TXiz&a+PL#@35OE70!6s0bt1Bv+P7TLgf{I3>tjKD=;We4`{W}A25BOUh`&+ zIcPa57p@eRo$5UTBr7r~P1QF|W6zs6I~sf3G&bznq|q$REA6j_7^;R}!%%sc;=FUIl@g@?VQfslcsul64K<9ShLZufvS^ZPpV0JC1TM*>J5X$SL+^ai_eAjR;S--6yM-tuAB-sF|v!vfHNt0;1B-6>jLfVwE2GVx1oW=5XvC;DEWq(l}ZSEhni$j>{ z6e9}qHUhYkMi;G8&(ZC3H1`~sdmehN;zqV;7e|}Vfj5zBnu3rb+NIJqSP(thAG0<< zqvel6U+vE_6Fy_3QyC+gk^?Nm+m-4!k%0T3?-m&2Uev^g9(94mE$SoI2_Q;jNP6W?ik&<`@a_xe99)3bR=>#CrCQ8*m) zs~E$#2q-i!{&fV%mKM`>u@^;!s;g7%U)>4TD$n(#HAyk`D10FYY5jiK03ds3Hv#68 zI*Xgr{XiNye0s1(NSn@eP~tMktm)1FF$fS`c3&KHFm~9F zufq+z9v*kzJU%Us`@s4fX*(=`U~RY#b~I?`WNo1HABnM|`trrMOnrR*!i3Xr)I8iW z!9AU9LjKR*DRMTfz4Y<+>1MO*wE-mp61vFv_V1r#zI)l0KE(C3VS@KJAp7jEpZ)!L z&wGfOJ+Gm51SsC=Y1bs$a71gS(!TSvPQktoJ-9>%p8Org=I>w++YY{ehleM9%zjI# zL7T*rDbqxh@I-awq;dYs9h_`4jl#OzYcH%n?69JNoqK#kv+Co4^OwQn8<-#06SMY9 z%Kp}i2l|D&Hv0v{4E^nr_I3%mD~fv`Xi3+Nc&pbkb|L+EF|N}+m|z09N5V%;iu`m@ zqLg~R4F$#g5O^n@^pVSdx)q^_^+pcXyJ=ArKP|cFUeEN7flld~{%fOP=QDg*ZEf7j zH{AO55R=<&dNrB8MrMM|LDw-A7ah-VRws^M&syrolMjd4G(t_;wvWTe^Yxq+(!>#d zzLrLK@Yz*05gF3@cu!ZWtHQY~SB`w8yFG!t^RPRww@%Q5ZhMG-9sBA_usa*$tKMD~ zPY>3+_l9Oqv5ECI>dEu4LodVjgvh2h5F!18dfX@oZ!ISCGqBV?vD<-ZzeJ*y&9`kjje{8z^cD|}^)#X;KJbV7dHl)ZbZDOTw8OihE!7VANElYkj9#b2J#nVSepPC)! zsa&!2Q}+P&?ryspU@bq9xkQGn_!v&*H~FXor0hTvm8aEl z@&0fP^sYj$x!=OvrPc}z=^uwGaLkXx>4y(TGxn$v7df5$&JCzb=Hr>2E}LX92<=0< zv*G2bcXHXgSWhk=KR#Rs{JTpb|MBUmY)UKF9j=}TTH29+ex04?uhWYu07Zteu_G`z zxb^g8ALg+k4DA{-MjF!WaWym(emuq0%+@EOQ|u=<%7_SU2(=)*QB57Pr%b-32D zkUiZ^icW@XWxoawzy0j1FTeic6x`K$@p3vwKnLRbbiW@KS zlfm?VGiUacXUO-yU3#53JxiPoBO~NlQ|hv&)DcM2+i`|IgMzYr){fYciSLLQMv_+Yx^k4V{IsQ;6d#=~iA}Fu0Toaz>Ilf^0+Vx{^;W=*LJFXuc#f}@sM{&h}_07bM zQOskpT#7|GKP4>ZhaP`<@^X>+sP44iRjtzOY$E+hXP(paP&FNE5jPXa6fh~uuY`(! zRR~jTzY9R@LBY|U6&P2b3DHCI&Z*p{@U;vHV_Ucc$E_Q$+`30}G7(_jJ&%Ae&ucJ| z$OwXFm|2SFG?b2mOkM^~6iJ(DD zKb8@ob)lgYqt2a(zy<-j_cD1-QVyD94jdNqV}k4iiNIRPzw%2;$)JiEf=_UN8axlA z84TA4LOC>_=oW~S62nrumKli?nI4%#Oe6kO_><{J^G#lpTEdEL#P6P?NEVa<8jyb$ ztj`uIZK!nXZ?^G}tw{HTT~{j$?MNMc zhqZ|^j`nOiX4e7~{(1L2la?cY0Xqa}p#|HZqjC*`CN6R25^4HUdgl09Gqf)lSDODGjAA({j6#A)uR4k=U^y3F5 zRzVnomM*SV(=%M9nF#2fpO#64tt4E8xlo{!gpOBXMMwFyn}t561B*QJfy7*QOP@ay zu3MpdH{ceSN+~Yt#7TvIiCUDbNcT<_EYbi=*bfC`*Ky#>5IjspEHj^Y3A&1O3vhF< z!VV&xbT z&+@OVM`BtG1$G70 zQP_0|j>7I@qNPx!f2;Plb|r1-og&8z0$Fz%J4i4`AX*_Z{3EA9$k&zqTYs@-@fUyO zJzh&W6m#ug``*=ZGBp#Kno4FSiE<~8@vDkutNJ@vGJI`OFN`izS;6lR~ zoJ329(nwNQlM{rq%pXKM%A+c3vdsv)R49u!h<%nuVQf{L7}>?-@Qi{S0qYo@TU=aL zZwLQF2l+gbg`0v=2_hL;j%Fj->QjI!i(g&|NrVDuP9pR8DG2ZaJ3lIhve(Q068OI0 z>}pkjAQHoW0}J;O0R`wjlamqKkRM#j5P776iUpf)E+=_{Ek{13u{mt9^>7bG&w}1E zj8Q%G7AWcPHAZgAT?>KRV_GLeSB>(3N9!joIW`pF1j;|xtj>OIw`iWdYNrYMWUE@_ zT0oUGf#wAGI1m{1d{lVf|8F1h;w^L2E%I7qLp9|Ji&f=f<1@{b|NPFZ6Vo3 zE7>0gDFR6P(cp0II6LVd0VCPX0daoyxe5O$3&#NB{Qh(EyP?GC>}@;`;qUo}+~U%^ zLz3QqZHxP(EN8o~d>o88|AUru`Vls`^e&JLhVvgGog}d%%zPP(lqvtUlt3w!Mi9{D zA1IAdD$mkdd6s4&Gz20{rm8$6%bXeBSqg+%BvS3Vjw@xOXcG-eH?@L_f^JHoCZ&QZ z#g|k-T^1MfYiJB}5ior(3#Bl7%v)ML>QW4U&+i5aH4D(t=|gJ-wqS;WrlG9SFRqP3 z>N?VhKA_#r@n%{QO_`R=c%gSC>rXHMj%yxJh|LfjEnC_KbP~|05fC${w7r)KPbOGO zRg46dD6C*(d^ew8xYL<#j8sIViyX3suUc)D#bYCjhZM|JjevJm$d*`kLkSc@zZK+v zeJEExKDRH8rGwgFn=5;1jg&YI)&P zQn~dv4u$1qvY7`G;RsQ+eF%(5^c&> zb`^t*nRT6|B%l&1)QJ|Epk3FvaC=d+PoiGtY7+inWl zOG3N1Cw~VCJx>i0@7ra)yKRtvWX}2_z&;=Eg&?{c1knx@_yRz<7l7bS0611z*}sm@ z=I2(M3u{9dN-NL}$7Pt&7*V3fMU+#!4AVuJ<5f`lVI=dnz_uH~MuLsblf65mJvu5u z<%=Lz+d2%4AuLu&DQ)8HSl~^h26UIOyE-^u*e}3!Iez?zSw@PWMz8a2*vs3sI>_e zRoQ}QA;x~yJlT)6SP&Vm>81epXo z8K{&Reb3v@rl5gqbt#dq*g6`o*d=}%Gh$#|PXLs9ZG>=3LE~V5^seDDLu-YvfGUiY z+kkFFI1b3Io3GruPv;tYS&nAGo4^MLRVIWVJ^J_*Z~v5T|CDe2G#DaPEOvBta!3L= zyFe^SRV+!3Sdy0V8h$s~Rxbr`qwzj=G}yV1gSYGmYY8KG+a z!cu|TkbzDgflBs&duwHTm=NFJ!G%m@uYafRVHquMd;Fv1J5~;_GqziBx&U+HISuR4 zVn|wiN{f5B9V4@kkE!a1=3`UCwxT+hJeahOMceoLg}?Y|1>f zzq_`UEHop+?LJtA%KV*M(7uy*&vMvx@D7mr89s;Zs8kt$%kt9uG+#nx6L^iN+nAQ| zZO3jvrAi0~1RBWb@&hX@?86D%l&`-!HV87#Y zX2>`(L>#7n*TMGrcLqb_;eJ2;A9wl_nV;N`%6bCL{F#}pTszsa{AG^Z;BK0*Ak~KK zfotZ!QgSFQl_-&rbeu{lN%ny0?A81yc)41!IIU?`xe4`Db)gZ@1nfkZ>XaVHsd_Am zB&p82@)P5$T*yn95pwv#4#^+oX88^P%KnS-Ig|u{fSd%3zwc;U0*MsJrN7ugcktU% zpIi91k?VZ-JcAb6qbGKp)X0HC49;z;`_HKA03!l)I}yEVx#MGHI%~u9%`lDdw+J{R z_H|{-O@a=7Og*ipwQQvt*w!{6a!w_ZlEP*{BeXv34uGHfaoq9Iak=R*(P$hcSngxH`EJV~#V4HL3h8)Ul#2+vV z6Qi>R8gDgPXf{)kfIZ#YZlmfIaM`9CLaT*cih3Rn%orn7WIi&n4-6p)6NOt@3>ro< z!=mC-J7VIfj+vk%wy!?=AtOdyA>JV3--TjrC)r_}lhVUlx-##SlR#9w6faQFnJM|vz z>3+Lj8-q`O&%vj^^Wfv(bMW!+JoxbU9DMlu4Sq00I4nI+R9&i0HE?~2Y2%GlW6h8v zyrHUc;bz?JX`YVrih(imgMK~6DB03~`noN(uTf+tdsL5kM-krW!C5=8%IijxTs8V- zqyLS}fSRDrS{0*uNA)=Ls4}t&qi#%SNM-Tsa&&Hw34b4%U>hd@*Ld2u+r!KtEg@uT z0ah`gFdl@EjmS_y3c{I38w;_VF!m)EoU1`R&P@2Ct063LU_u`lrNl@Y4VW;0pL~XW zCcp^ih;eBlBc3WP>&K>T{8%82k_LM=#nYTT2n|2%K+bP4kre9?OA%a0VYUbYCq{^u zlWW{h?jTqS0al0_axi5uBBdG{%1983Poa6?`NAMcF`SPA8?X>)hy>8lv6q^6;IZ7r=*`nVy;2k~y^VWWUnYh2(`jk&aclfx_g{sgm= zCW&$-So5yW_33+yoN`_6;BSyCcrimEA?v}q>8LUTrD=SXcWOIKWP}2D?!X_0NaH?> z6&e|E5EKdOh7Ux5kVqd&7X%Zj1vF#uCglEHH3(=8$`ywH#4R*mT>{B@0ujcGCg7Jj z3c|JUx$&jB!-p_W6wR@J+LakiUBNR*cz*KuX=_jun2GBVl?Vn6=?ck_`w8TmOuEAk z12U6MBFGRu@0nfOPm{`3*#!Ga;%N9%UO@|zlxQz(hIC6pasK3#atA_jI+P-na#MSg zS}GoJ*63R%8T4;i&=*l2B}84+8?KIu@*mW3+pW)0%9v*j4#k;&wNQF6e1LCk#Hqi= zHu#Mx9FO&PTSHj^zp*d-WN&zxfs8H-0+sUID2i%PJU7BsZvATEYZrPxY29clS zH8DG${AXeGp}LHJlyuC8%9e#<6VBvy1Gau$-PG2~b={;-VZ0%HCOd^sf4lIx)2s4k zE_pZV(P_F&Y?r;_E~Mc=GjcRzHacCmR_eM|tLv&-H?&&aShc!fMzw$POjW2`vTL-u zF4elO)$6)O3q2T~jV_Ais19g1Cd?Qtm5Gg}`(mp44|+L&g4GWAOXiUw=p6knhmmBz zj|P+uyPPx{cDu({ga8DxNq_}qfGCtqSry4j@CA50>u~U~<&^<1l8J9UOUSW9>6?ZE zOKwQiB5gO{l4|wE~L+VWDzalwx63K+Fjy>aP&dHRN*4-I1MY;{xz@h7k@$& ztn^>L;{Dx!<@9{!{`b|Yn!bM9j@a?{`FHv^@bBE`;OXxR3(0;%NNy0q`o};@@DU+# zZV!r3&+E}y0cRHXdO9xVARns-#HJXcVOSG_pppa3jl+5aMu51!61j~fOmZ96!7HhQ z5>iL2$Orzc_zjMf9)oq%F&q!(FSiYxozt`GY6(Ao8#q=gNsBD%!&e;LBN+`Ah!0%( zYKJgcW?q}ypu=9YOt^2j9BkUgey^}~@q>4SXAnKC4&A&KuX zO&6noOZQ^*W3i-FjY{q$w^lWZ?p~dOB!Q@^GHa&Vbnd(Gs08~?Cw(5hu~BDf)g3CuL`1Rx#O&&{RghN^*e^@gwkraKwjSJjR#v z7>WX!Hzb|mrU{_K1hL)N8*^mOU@^}mP=V!t)`3a@ztbwI6RX&-QT@!vq)uMKr_`o5 zNkH$TF*eFSyJa#Bud_J%sK!b9w2@Pdg#biih_N@ha)K%?35DR?rCj4x8B+yiRc3T0 zB6O=8yiI-ra#3o{6vXv^~BZZgDw@FrH7<|lyWz!5?vT{`G7-9_z|LtR= zXTl{oJ~s?ZWb(b~sc!Pd`1Bc2jFQ}OjK@J0@q>x2;bNls$Y9u^M+0`UAP}xfq*!Iy z$g}`01TtTUbQ)Ev^BOe@ktA!QQw3U>2p^t&bsx&6}Yp1V>$)S2C z>6j1y2Kfhe$!wphHr}JEjd!VPxpR9waIa+&12r#V56M-n7pq%~ZY6n8r>H=G8P9xo za!zy38Hv-{e2y~B%c?2MGeq`J503AM6On&P&@g!CG6p9uhh2>Gm?0jM5l^`qV`{Bs zLa~T>RLE@ObdhEGE24|2@^Q$^_TFGB=wK@SsF-p@GKL3!8kQl>MaZX{p`_{_2^(wZ zB@~VmYtof1fos>l4g_Yp5Md90!2u$Cb5@Ixc~Cb9tu}=as+5s_E{7|>kZuJBc_;Jr z7{Ojpaiii&Lu4uU(nPh`0!{=cCTOylq2{PgV@JR=R0G#ebmmrgp+qgq*ptILL>8+- z4$NP3H6(en7y>{4t!6k8?ji~EG9)__cS388qr2=w*gyQ14w-VIo%A>jf&XH z$!w>TZ8ekG$y$0~;6;Oj(YUs~*{}LA8Jg<^MVpXlVey=NJr!Y-_7ABSnc|Rc22qcP zzNc2HfIh8ZlkZTo9oqbf-ZrW?-(zaLQj+K2wultn*tUla3|3Wf z>Ao7B{rG0I91ob+(n`4doUX&HgvKyJ7DXKE>Ur%W{hLz!wW=Sim}TBpt|_Mq#|S$j zj7oP5hBRI662?|lXQUr#c910|l(QMG!(}Y{` z-_m}&J-Q*`mZP^IXe2lE(x}X68OXB*)FnXj}-Z5sF{^v_W z+ECIazM8hRJxwfsSQ(MZBR#r?ASorp%H`-^rDfL=vnD+>RV&vthSsf#B@TBLY!TTS z9Q8J4V5f(hM}>tKlvJ&3ae83Ps<#j2B{jD&QfM{&FWc|eB0KJ>gPP7qW>)e^uSAE$O!kZap4& z5Vr7|-Sl|akJcMR^52~{N?ZMU&q#~^F3zZ=5msHea=(l{GWCYIW5;Zv2?7#SkAMM0 zFn6e*zKR68)yl?+`ldQ|Z9t?jh-rDUMY*F$N5;R~V zi#>XU$0idG%#OK3fr1`>I{4>+i|IJqZ-t!ByAm?H1;A?3s{s_%-J3!8o3)AzJ_~GK$tX8;cDd~ zx5A$~Od)0{^ycDqTQk@8D5Hb*zJfJTwP>nEQlzNn4uvg+e>#-g5N>_NfFJKFVCk%( zy{e%|CF^e^aO)NA2L(TgKFHj};R>|M5>h*>RLO3Dj&^6WE2RER|H1T1M^UY90q|)im1fG_BLg@rI%4eD>$Z za+Wzc2q>WM7%wU?Nk`O=#D%p{b9+VRMu>`u!W~V@f5m+ZR~X>W#1H)ijVBNje1DPP z11%CkrXXcTituZGUOi@DreIqkClMAS#;k(v(dZ{DFXy#tm@CUuw5W@#xh8;V`;Uwu z1z$Eqr!|>47&qq`_u&b2V>)L}1}ASw-k9kqGa6Xn&@qWm)XOysaX(LBZ-L2a;v(82 zMpX(Le^p9OHDZ6ica(o;@2EakC85;m?DhQ5&o9+6PPaj%^ARvf5Yjr&$Sw$W&SV;! zSckPlf*;mOxP25GgK(NrFbQHEpWja~tFvWM%vO^*P0BaST$V@lsz;c$ucjNduo{On zzME*NTj$S1b(c4Npr>}rmD}!A!MRHwwLEe(e{mdpW7F1P>}gDRRs(dKT#%>;O!4-r zy1}Z|)3pv^ODZ?G!sW2_CE1?H`X=aAWYS|u&j%2-z z;ok2_?pGqYEO62+D}9fCFUdAq3In>w__g&mQwqswv-Xf=H0-sSqG4^JnF+ zr8%z!lK-~M{j1UJ|IW$RlrEl=5bW3~f8#8527a5fDGVx;f7(f# zb#1$JQ3&ef&pws0*BB!2Y`kqseIV#s2ZsXMY-RdsfY*>SgaXOz`DFy=OTgQa$w=C) zZeKCfH6#({UGXf+A5Sz`YNoHh#w_%AlPAi8#(dD*GT=np`W#Y0>ZOOz1+{E#2 zuLZiV*=!ah8ULe^%z`Ok0df}Te@%zJIy=Q^3Sx9_IBL(hmdcW3XfwEOA5MNY7y}89 zkZ}e+$ElMuR)m(RD7X%o#ipOEmc?rNu5e#pm1S*o3rw!mT~ibGdLTr`wID}cUbrpg zymGGhe6VML`UOF$1kNBUQ3==7X(Kdl(!S|g<09>2>VfH*fcYJC8*bR^e~gi|%$Ypb z_UD)krMwYAowl08k4G4E7OA=V)eOwEvG9%Ws%Hbm1EaTTp?GyMQXKmpic)F$F}nD8 zC|JY5JKD37k3$U3uS*Sr=HjQba&$5JAE9tey+}=80WKV%E>O3&$n1Ymq?J@UddPBs zBC0f}bx>7v#wZdQU1Dk}e?u<}H4SKe@Nt>Z&hd8LKZaiJ^^~5&39f?hL&NB%OG_;NSk0??9R~Nx>QLVZTZAvK(RS|lrA0myZiDXlz zW4bOs{Em2+E3eVbam6K6zzj-`0!MS{d#O$J?}u51?A3mp&^gjae`*h(sXchTxo>jt z^Lhr}>QGVfkfqksmjNKI#w;pkc6PaexhIvHg1m3${X%=yxxve~t)m)LD<%~|i9i1j3#mz&+o$Cpfn=57+ z0i?mmndi_mA=EI1e+Yx%%!99h&cf5{-x-7**Y3u#IkOV4g25xQF%OtgZYQy(Og9QO zb@BBo1lprW73pS`SKNXY429xk2{h_R8q*V5n|4Fq+YA;b)%062?z_d50z(RM!25Y( z>od%;CcegB1hLtY-WKlsvS_v$8)OhoW&cL@_G!SL zBh1UxZMA2*P%H$DDaf)+zUAtTNaBJQF#%-CeuyNoCeok`ghcmxvnwQ5z3SD~J5MWD zuRvYU_nnF`e|R?gv^QwfiSAQaJ9?3-reN`L-;`w~ee1Z9>jl*e3=zrzTbB9^3Ce;> zoy#+y>=(282*rjr6vi<>u;F9)U}Rw<4{w9 zB<7LV7GQ76zyecdRR>N%wO8rs6j-7^E9Wb{ZWf8_$!_^RAa5=ptPP)LK2yh}UBOWUCO2$Lt`g82O z%%h=qe=G;)^pJ(J@XRIbCSQWFZDI4lp2utEnP)M#H>PVeA_iO{p9M^5$fSxuF5dPH zU56ZRTjoC&$hhyK6v8ikB+86Fh<1Il3cU0}_E4x3G_)dOBsS7zB)%R~=(A+%A}a1R zY`|*WHK#ZCZftnl7MRnluwRK~Or38=%h_~xUUP_CT}aD;6WdD? zFiS$Ih_@hMnQ`C5cj#CWhP6q1X%jvldzKZRVAchgFr}T?)2Ke4TnT*fV#Y>?(T;nr z_FwT8BH2Y0N$DUI^a{J7*EL$BvE>sjn@HnyAgl*-S_HK}h++5AmWCy&na!G8bQ^XN ze+>o5xXB%`u-s6fnJP+NRS8c|tb0=&qk%HcyQ!oA{2+7imN*$Z+Z(Rf1cks z`~@usY%O29bxXCQcBn~iIDw{s{oW(p(kT(Wv|U)ux5|_)Z5w)mv*Aj+X$^ETUryi6 zXO#{$m*Xc(RQz|5;>du9KX$d4?a!^t)Z2IC5Z>}07l}^5Oh{d^YHGb&DPWLyq>yWy z+N3)4%O9m;{af1a$`RHT>kt!)f1XheN8)Rr(fC_D)^RD#m@zcIoR<*@!xy0`MZ{g~ zFdKYm%a=%x0y_EK}ti3P!%xm18rX2v3)pJ9|P)efvTj{XHR$Y-4-G@6P0m zdgq+Y`{FliT;9I3A<;b_5olDDB~`Owy=CiV9=Vz0P`fOtwh7Y(gXD1oe^q283r5dJ zOrC>~@&Mt7WF5kZ9LPRArx+L>85xEGcWC$&O}LR#mW$Nbo?DU9HvtNv>XrP^A>WIh>g4&Knggr37FEb)-UKaiizEe5xR^V^xS?k=FmXO2gv)LrA0)vtK+W@ zO2&jihS2}YB&102Ekmz>e~gR~;0{PD0Y^mTK%fEUKF@1Liy&qk1w15^Sj3?wCUO$Z z&#!ljkvtR)9CGLxr!##(D(hUsaIt^AmL05ux`g)*Mo9^EQkrQ-D|wb3g8C|e$Ltev zSwFb;n=R|w$`KP4@CGzwI`j0wbsi%QLmzs+>HSq-@z5SA~^dp?^NtuI8 zGN@++ZtGLWNbSHwe|WJ8gE72NK!T0=6!KE53ElsxRlyO;EPhb;$z~B~^4bzPc@Vpj zlF?}r7wt2@+&!))-V+mQCfHhrk~&)#&QofGtSh3Oi?W_3%p0=pslQA;qY)(?R~4^z z=7qr$VjyAoVC_jDwc>JH9iFzz%Ika>c3GrZSEmim#hgZ-e-(^*$K=|%&K)!Pm7#`- z&Pkz9lb9D7m9Ekka`l1c5R5|t zoWN#{S)Pn$>t_ISq&RM?^66_oAE%vPXK$!bij0#krC*WEx7icFG`s$w$V z%&4z{5SdxVW;T>2LPH^SI%XcdVQzT^p`@58x++jwn1S+2YKH>7cfvP(CWL*p1_Y#J z5HeE9e;_ZIle~K#rZ|~nPK=A(%Dm$QpCRNj2V%fMGrtCA7QWmOv*lEjfcdYg>8XS# zeI({@l-f%y1uhPlTclS7WH^N&J<`8WXGGg$mS4xr;)0_LS*j3T{u|MVv-fNujp)&@ z9s|OYrc|CIG(2V^G0fF9S9hqt=q!MHOwIDAW%je zsryf!2F&cDkx9%}Z!9k!4L}elkpWJ2gKIYxS@T+V1Ih~soj18K0{z!`{~NmD>NwIH ze_xfYOdYag;ARshOqIo=Dc(R9poNTQ(sjUFZ5iO99v=xs7G=e8(&^S`lKsVZk1dVw zn(B|#QYXH)vBP%=3A=SnS}P+1vf%H4DLW@v0Y3OGT@sb9 zkb2Ef-Zu-Wa_!o}y|NkX)L0%$L#3f9Lu1(eU_ceR(>>kNWJMA+^h)A2l&pkJDkrt4 zqguS>YH`d0@)=*FT3e;m(h#sxvr;x@m3ddxeb8O^2~&pwRmLZ*{%$@S zCoHG34q=#>?)B3*_Lbm-Uh90+f3A_WXQX<1b+B$E>OU#Y1>a1$wQf z)o-(VHKuXev>vB=Ip>}Lt@CpJs&$2ReRWx&M-K9LQi|k}7^t607FEngf2dv%t;bdY zw^h$=S~HJPq6N%%t;cH8nl@HHG#{H9wiQ7&{5ySSv$v;zQfDo<+(=*?KQ!lV!V6_+ zT`r!=b6cT%f0+0{J4sS&)me<)t~$zK8@CfA=$?Ar3Zopa;+uWV1H|tVj(%A?y0)TcF|?JwJdLTum#S@q)W< zq@WvLScNRIG0(pGeJRnxA0Vi0aH(_v(^qSCjfD#4K!H|iP9+U=;_ zg5m^S8+cb`;J~>XxX#U9Li&UYYE1T)oHtx(e6^Cm%RsgYe`zl_!JKrSMFMJGt-%xw zp-GTSokrZ;ROOk(!T<`%bTP5HuGye*^>gT;2+Ibfh_IHSY&KBYYTqo2+1Z4t)v-Y% z=V%f+c>uA8sU!m4Uxtf>)tDfxPz+z71T>W8MzGPO*6Qd4)G^n#eu7%nIAh`rNa%`? zb8oTkB#GT1e|?KKod3Ka$M+haGWh!$GdV-hkU=Mxg@_d1NTNjT5GPQ@z*vRLYP9-s zrAdY1n3Sv=={9eM@IS)M5dIEpxUo9=o&aN#SK2I`G=Nhl`SIX%?h0rEIAaTE+=es! z1K~3OnBiKIC)~*XdRkV+5+q=aQ29=s$96}Pe79Pxf9loCBbu4m3u~VV%r!G#h34*u zT<_DnDoc${HwiX7!5n?w2_^=>N1}51r!N+vj#KFM+((}BJhu%QCx@uXM%N@y3wIhH zK4$ERqi}IR9};fR9?Z_*6Xd>3cSmqo0Dg-I4gss^&h4D2e6t_{Ej?DX;!E z-u*07xt$-jh+h3<>bZ4m_A%mRK|Ja4-DfzJGGVFqUDXffW7U+mZBYIP+#U}ZU$gUh zghvB6{g}Dw;UlV?IO73Y&xqkQMcNhhIaZ%8e=u&oR?_-z``LbXn>x_U(i_%;*D`ZJ z84t=twGrxdo6sfRwV#yXw6~y9D9af)sKjpoGX;(IpGD=`oZu7 zSDl4;*$9!7sFB0Bo<8PTA6W#y7j$xUUcPTgS&bs)Gcw~zVv)3gtc=L&Y1z4J(@1xFSuD@WDYCl8>Q-DvaBoVzMJ{Zo z{6Cf>?xMrxE-^H*h9)Bhcu;8^(JBy;1@i0#sI;M~Fc@0O6`9M0lcio+8)bHN!r2uA zNEu*3MibZMc*a#Twl&nx6t-4~`jI6ue_i3mjsa3(Y_h``3j{CZD#nG2l$KtYHvKXd zK=9u}=tcc0Qs%y*--cCv89&PUrdCgxVd@(KRl+0bXUO|2aP^~gY%`l)T+7JjQp`w& z67zQv%J@Q&TEoPb|I$TBuRDfg#`NyDOKCykrz6#Z-cnB5Y_WRn2IE+Kor}Oke=y;h z9tr9apDk_a6k04JbIzddl2FI(^q$kg* ztIb7@MYeu{HB&#+ltuwR3CmbOk^dHQu;7M7A8x6jIJoTyrN!|g(;M3Yow$ta+7FDA zd{`MGi|G|?JmzT+)kqT~43yG4e{EKY)0&&atdEpNI*r_lEYn0gP%&CoZgFv0z17=< zaNAJ6kdY)1|MX@=#NW}aS+CDE#($fMCy96gBBO4rz+8XA>l82`b0!g-|SY%vwtwSKN4M(Q#g+8bSP0Zaq|5o);7=$2Ok(^U`s&^fG8cT+Q(k^s}n|hGOq3R-zYCWQH5znd6z#rTV z5gUz#7MCprE%0VzXf_h{$5U;L$zfow#|i2bX6oD_M;9=Z#73u+gmfiLp(AAUk8usf z(utx`I2y}9Zuo& z0>)-WI|9V^B(=;w96ES;smB-&WY);YW`t#c^h+7snq@2U5Ht*qf6NcSL6-q;^hk5Y zU6`R9;R-uZn75?+;3wEWFkcs(y-%fau2dKJ?!bSDELV zY{Y?a<_+qb9tnCFf7gOyY%1D(#jqslO)0TKpA~k2LP)zC7_s>ee(PZxm*rcJMH{&3 zBm${XFHYMynJ>qBQQhx?jG82)Hf;v=7^uc*4>&Mon_=c#hq*q=-)-9cjm6hLjGaqh zC!*R!URBHKtE;M5{T^e@O*oDY(cYzlAMF_r&9|`m1{`s0f06kXHQ!8(kE+~^hmL;h z9ry1x&p|!-4M1B&&@({K4t^6cxgGlkJ_o~ce$JyhZr|Rf7eKWxhN@aOM%8KPZ*rB? zE!~%R$@SKdk9cRu2OYfca&xSRK2DaTWe9^vkf}Kivqy&>Bb~w-%zk%G0g4ZFVxsdx zY_opeHUU!5e@=VOM#+04>(uLVw4#L}hp8e9N@R(~l~TNHisFkABFHBKsLab_XDP?!{USe!iQMD z;Dh<2Zz0f*c-die)4X#b#Ed955s)^Ajb2g~Lb#8Dq{RY-^)d>~G73|i0EzmgX$Ez! zbS^WWn?M-vxkWv^$*nyUd&mU`yV`Te6&dDtqLPng`4N&0mMAl4Ylwq03S%1$oa=q# ze?0GCY>py!TgUdM8p=th<~om{%~)mj#=z{1&GzZ((zDhErsCmQEnq~*g~7d9bziCR zFY%+_6fuL><^1yUtvjBsejM1V(=>8Ei*Ix_5jj*|=o5K2xPZvsE#+BI4WN2zfYl{? zY{suv;pV0wD+O6L-#`!AA9uhnnQ;&~f7ODVA#`A2kUFJvK}P2;5N4^u6|T72^0yv~ zKrWP&g!}W+MhtXW%SY>coP@MNp31?qEg`Kzwx+^fBbFcm4mBS;f`j2~5=QkAt2Ey7 z$nyie85J7(1pWv?{(B%D=!(;zOnGp3beYDCWOD=f>eb{srG45}+O)Tm3t@sTf4^yY zXXzAd!B&o?)0SNy*zL>iPbUl2x=*VNM zBM-Le$j_F=cv>N|PxtEHy_Yk_b&i>x43BRz!rUlVvG3m?S+Q?5MNXX_#wwC9({Srs zj%KUZbn8;v1EzwBgoG-s?ZIZMi1eb-`uh$yCyu$Tv}%UF=DzdJCc-hXf9AK;AH|lS zBHLQeex3Pi@D7)L?TV0CJHo}18e<7Z#qG;ZVOjyuB8?RNjl*0v0**i36DK)oo|mW;Kk@Uv;F6tNw) zwEJh}{7R!F%lXJGWZ022S@w+Q2sG1lOiy5gV!CBSBo`!0ix$4hMaUMHk$G2Vrpm1! z>Tg*4S+k383A7CN2U@D=068^NM};uaG=T7A@Zr@9>$pf=$X}zkf9hm(#z0gxoR~!f zNf|NXG4o!=+>GSJD*TA@cf80a-g}SFddSauQgV%KiK4AZ$r;^ZzV{?0??kt3-fUCP z;?-p{he3A^{dxwQ`J*6;7|7Uq*nyeW`2(lvDGJ@3DFVSe`__+v)@P9l!^CSMt$dqB zNAD=3WDHY0PE)u5f95(%G1Fhv`hy$JRMwuUsLrXWlcf`1nR;Ph0PCr)?zHRFz)d5Hq@Jgf3+L*(t3l;PS6E3HLF&O z?Vj2&$k~%pghHZFhv}qojWvUq89wBx`wu-rD(e}-w@gb2F9+jr=9}P%63#yP0)wTj zrwL(@@g=~`Q1E9FVJ#7%jXB}u$w3|+p9znx+g51eK%k`$8O=lziKuu_9X+q0R}cX5 zz%#%zOR!5fe~ZYm1^`WWo9Q$%(=!1N9p0ZY_H(;XtFneG z`At<{W4;l58OMHo8Fi*XLy)!OOjF0e>=>RL42jP+l#p%6bhzzq5eB$ zAn1dx412V|e8`kT6^Oh!oC?kaCqm~T(wHX`ETa)k5nTJ6+yqJqW-Z6z$JYG9>==RG zmxgeXobIZLCRU{fSi-joduW3l)g3Ujv2rc=!%g@d4nVr+u8UwKbwM*RfP|TgfG$Zo zhOj+`e*|L)F@{ve02W8&O850tuf9nQ!$&S5%y80YhQ|#f%f!k1>one)a5PTUh+q(u zw``{>zL1kh0`*1G3{>is6yWt5TNejHhKf0tb^)8WRuh8xEd%z6?_6qREF(%vq)ruO_e#1WxnZSd}`eh#3*v!zYQeCw* z^U0Ks#s?>aVpEl+n$9fM1D=(oZjjsHH)%i6l!TD1gNVX{ru&>ALnsZiu{HWDi+VP{ ze|&3mz4(sD!3|h!))uzVrCl*qs7NzZw$@g;2PHA?KWI;c2wVk9DxUtkPnKKp4M;3&8wwPxB z(3}8as7BnBbR4Q94%1}`Vks>me-oIDAmJ6d@-j7>3>T8CD+z(vTua8)L)V^NdXezv zej182k>QkM0KMUpq5LMdD6lc7QH;oP{F%TaWbWgm^+{vfdH)_ITHN=7%gmJ14rrp4 zKkw+umeK=?5bDi1nzzDu-$yphlRmae#F34X=l3|OYw2VCT}FhQQd#ble+cXFp&w2I1tJX~GJB7;}lv01&B ziJlh(z!XuqzyPPRP*X1iYn_%p?j%YmQBBVZx15*d+d3I#SZe{9m>Ee5vV?jvd2V~X zF;zzvGE1mDcYNkDf4wqFN@Stc7fX8?5>ez88qJbgz`BOpqTQ|-IcMVSg0y3`!Nn9Q zw}L3UM$%RFLmE`e>1cLd7ELO%EK%c<2=Cb7crmzUI#&)etfMkb;zAsUSYF{`4c=I&(*K_1bng1B}~fBy=lZ?zYuoDL^y$7V+w z(BiCh`)XcQ^9vKp!GpBPGN9^@pe8>sLk*g?*emOfP151OWNc+)f#2!8nqS(>1Y{l> zY}uT}rdO+!3H)yU0@Gw=x~Ng6Lnb_Gn?rit%kpZ~=Dcs>-4x#aI@XZp&bwbM8pb_F z@elE-bTFFTfBsa1_b0EfW@oq?&^a%vm(#QP?B(kE{Gb?C!|8B&`1AE>>5NVXgOls| zbnJMir>E7!hX>W^z&{F$i^1Wrem|A>Q@jtl@0ap^c{(_|S}u!O^=w|wmxIF-v^zU} z;Ga~>w?B_g%jrsc|E~Dqz3`G}lY`>$XZgLFm&H-Be_YO&2ZL`%%hhyt?(paFU|1Xu zr3vr-^!35)a9LDW%b5TweLayOIB%xoYI6E$@VI(Bc;rk9tbhER;^+0Wc=P%Er_)F3 zNav{o+@nvQoI7g$oG&j1cg z=|rJ%f0mC=M??8%AVohxS^0A={|S7b4&K+OpUy6?s&To2_Ek}hq{Macd_EqP2LoH~ zXi{C2gW>e>WOh}S4^Br!{phGDiwnW=kxb>6`ndZ1?Xz;UTK#QwQ5*~|(2QG6tFjmz ze&_u#9NoN9@l&OBrbpA+tXTf*zdir!X)zqNe|`z9o6Asg9zTn@--UJZ-ru_0s-=yzLwwf9|xzW#nY`F9?6z-ad3ECwOU_Q z^JVd}oXZrBN7V?}KKxmTXK*ze7q6$YVk~V|Z!e4aYlo#?NQKdPVQ9+`Yx~QQ%+S|W ze{sR48oIUR-TeP@-mAo!5}u?Q_P07!M(0p;9+(gFl!4G>P%05O(1uZmr8Jm`f3 zy{aSjQKxHl@7n17ebXZYObmtVoKCU$m3-I9+!i)uUL1nF$L9S@L%wagv&)ne@Nx<` z249bblJ)Wj0I)0;SH-H5p&bsJwqPkNe`Gs4T2*glpBYbAm*wc~>0maW6@yke*@!A> zZnNMk!NZS(p{%}|z^kX#@$~TNi()qZs;K_8sxA)(pRk$1@N80?{mAFl)!A9GS{)v5 zSqSR|fzRUff4%tTZ%3E14HokIr0%jf8eLTrj8{kdvb-)IJ{+85#4?qApPwG=f2mE3 zn@#&A3cRGMwS)MqL?LRJoH$OyoKNfGF&Bw*Vht0HJ$|m<_z8u?~ahJDmE>_PhBdu)^!qqF5)819#RfGmFc-4Bgs0F0I`e*#eYxprHk&9%R} znwI02ot9r-O%HUs>R#1j@8!knd>i8WnPhGK3>t-(D-mhEhQ;^~0y0n9RP z1@lT|@+UH_B5}&Zojw{b=a(p;%{+3Psfh6e(6nEQSj4${^yy&8+OMvviXtl;5PnbQ za1)W=6+axY2vh^}e;FCV(?=N3L`do-`X$L62)WLx!$+S!oV{9Io|rybdJOLn~iRB{EpNn9f|z!m;cu^AqQCe>5KBI$tPokDci9rxW{z zN@jhvgHGf>R|M_RRav!YLa9v&P>D`yV(5H7Fr~jAI71R)O(*N4?CDzWT9?i}RA@Uo}7EAB%>l2~L?(_QIR*jZW82x&i zo9W;tyIY59e>YY}TwI*oY=e8Ux*Q1#w(OM(&|@rR7Q5dE|ET+*!NsE|BWJQKUZ4I` zJ^SBxSBFliT^@rkUwkVo+I%OQf;_E4l+(m*N?^9tILh1tOmQ9 zOjnjWAy+d2&vN=m=BQhvTT*R#FrwC@22jpue>oiq$*j(n(^oRkx2KOdFxA!ca2GY! zNfm~qn!;;?C3|)JPoq4FqCnX1`D%)}J|4XiY5J-vPSmC07;HCr^7IJoh)mCuZL?$K zOYShuoOB6wV6qk*u5%-waB!d6m+Ms76?ZY78&M^juEuuI*SNb}wlsHCPS0n@7t`^$ ze=OG7v$a*ZEJP@OsoxIj*$fy^_CDdFq0A^1-|Y*2&8+t?kT=@t!b zYHn`XP>+~&%a(di6x-IJS^jLd)?n3B+0j)to5GeIxMlronp)rTt3zQQdp>tgr>Z_S zvEQ|A7G}?TdSE!ezsgo~1bP}xXRCuj_h5SXuw6>`zV$N0?G5p)+trFGpgtnof42Rq zUjcY{eDHzQj2&YyyubX?E+U&l7ucI1Zc7tQfx}_j17!1vZIMFH(NO&$G*EQr^`tr0 z>=&>i%leFCfC99&y*l%6WNVtgc9z&oK-7I@{vz!==)gG}&1Q391Ph1B;~!u=TMxRn z61%%gagbI208hLpMK9r2N9{2lf196nYukPBLeFf>fP=xw78SQhTzW8jC)JZ&&fv#( zCVoa&$7b{GKuaKo+y(o;^V87IP=6Wwb*Ot+eA$tgQm=#ID)HV7-U?&2l-=o&LszHMRpMpV?pf7#UBR6ipH z-lj6V&4DGE=Yz)w{^LsVAvCX(bUKiIWGRP4vz`W@u(3|@$AhDD6w>)Pr3zzcy6Esu z*r;regQk;i2kn+x%?x%dt6~EoUCkrh1{Ep2+J@;5C+jkPy|od3GKMmz+aZ{4gTQK1 zZU=(F!)-b?vghsk)rq-bf4Ay}fbU;SN3h);{%kjn;`D((Y}0h_{?<(X>FLhh;Nipm z`n&4xs;9%;Qjs6G)l}NpQj;II)ihJOrK*1Y<(C7&oOgIqJe5+fr^}12ht(IOYUEtZ zUr%38>AcrpXzsk7Upa3^Ghw>t4no17O4-Zh9EMMY;2CC7C-CsNf6t?=nec7(XosE6 z>3Q1M!to2~_;1A<(h9hNw(HmCH~3sms_If!@x{f}toB0?&ZQV1?mojmzgoRLIy%~h zkZ0`{S0bOwIj-uj@~xvC44uoe7_ADy+O%>;v#}#w=I( z3!~Amy5Eqr2O@mzf3S!ew^oM^8^1Ega$*&-$R+%FIe&HV9lb_Y!f(vZr?1~0Yyvuj zoqITN28TZke}*V`JQ!VSsG}hDlmA}LXM^{LC$E7mtY6mZ@wG@;-?34 z@J$bo+9%A}>+>VFAxD6q10K($aUuAEVB+EMxZ*PgcOaYDe{j9B@Pb-EL`5tWVGRYy zVn&yQY9eyrC*Zm3mltm_P2c?>y>7New;yz6d<#%?C8EyIBIh5p)PK92pUZ)~`a+p) z{i>g=lThoGbro9M#<#UbmmWhx@S3`+Mz2apuV4e|wKV=j*u;4?D*wgzysjs~zCD-At?bU#345;{)#lJQGyHSU;UQXfQDh-Cb}G-|6+O8OskVQ@Z_( zFkI=5!=PbHcb`<2FWt4r8^(0U0b)4Q{u0WX9_yF}0|WbD(0~Z`NWmBkOlMG_vCZoJ z$p&gIPy7A@-Ameg`Fz`)w!LzZTZ9GXxa8OEXAem9+xj-A;Yj!B2y0Z)`JvB`Y%>c55as+IIMbVS=jV95Bi2FQ@? zaB4)jA=Hh6=GU{XtGsMTGkzF0Rjp`Jzxi&xS=axe6I0P1u8qHNyeI3`p6+b%7{h(v zaeJ~Yf39~?N6qwZ3hS(6CzeGHq4m~G90Vh%wzaI3TAkKtY~yQwd2l$?s19e^)4>p~ zsco{hNN1P^W3jF-E=S|jpC@mYqe~)UIFT3TsaBWs*Q-_;70Fh|KfjlCIjxRu7YA?? zLz72r@pL>tyW+6UmW8Yc6KNy@hhP=5h%H){j`m?pj*jR)Pf z^%#6M5w8EBRiRs3P8{wrrvtA8z>%@GeL0@;Py+AY{yZ9wzl13a++87u%faBs;w??P zwwV0BTsEEA$=xQT zx2MZyp@GXU4hBe1bvZgKmIH37vJwpgFg;CArcX}ulc{h3B6SXdk{TQ1>G`x0Zv0^I z@SkwoS8yT?$ue&WaLHqN9}cC|SMzEve#!^R-2qDW(dFIQ)y z%VKcMAB(|p3#tPM8^!3y6Mpkd8Q=U3%-_)bjm+QJ{7uZ?)cnoN-@NXF2)#Z~Xxd#J zHAUuC7mCkEXFrm(O>18WN{>6#f7vbnlPUL~btw{6x6qj>^lVq5b5rQ6Rv}Dox75m% zdSU*43ICG1Mu)~kSbslM9CYSQ8TH=1^2BoHPt0eXemTcwdcfcG@36&j@B7E6#i4)M0tqPwd|QalAUTMBD6^MGZ;CmScaWWxTLnjVq#Vd8P6et`mD&p>DXIaqA16Z9fkQ(TFDALX zVD=Q^=?p^1%fzxVjq`(|H!K0da4`_iUunD!S|e{S5)6Vj}|>d}?Ke(xIt z#M+x^o*Eb8ZmV8xDB(X9#liF;uw z3kpZ?-*GwYe+RlN$z%r9&M}4w$Iq((#uCu;`v_+Ob9v`4c$dJqyVU4g!FIgq~+1IQstV%P1Iu z3>f>*#DVW|XnuYf8di7+vkEWdOuTGasXR=z%OD=Ye>#rmuv8F3Z$%VgGQ7pBxD3gc z^=cf9=hLx2O}}t@#L;E&WhgEw+TfQ=kQ*O+_sfu6`O`0?g5SB#{3&XBzF@^%$Co*p z=TJmXg;pMv-jWcA@K zPMk02skm8v5oaX~w?51s{}UnE?LG&?7p-W*e~?D=(aj8xYw1lm`eVwZl5S_uTm{$D z#fBpZsx+5}+!RYnXGTzc$t!0l%eQbEGO>T#0c^wLF>}_)9-g29VNnU;in!m*5zr^ML1aY}e)ct~mR+4qUWYz!wK-g7ICH?R4RMP+M zmkvb%BLoT8e+id0MFAloOA`M}68}pQ|4S18OA`M}68}pQ|4ZroA0UVSC29X9w{}GV zR{{Z}x6Vfay8;29w~9&u@&f@+w|!0l;RSzwPok;$WcM%Q=q`c1B?Qs-_>E~lKFTR? z#vr5G1Ir4%)b3#T8+}k5C+tl8B!x3G9s4mn4iS393^M8WTL8As(+fw7!S1uBy^Z2q zR3Y@!4v-s6aSVKzDvr5@2jVR8c7a9DFH9$_K&YH!g8>HRK@un`kGVxVo9U!*uGfEO zII35YpU5f6NgQ3HsC&e0w`3RwkYsUv3K>^CrjHtfW(j^&BnWeHIQGxxois}Q$!4$H)j3m{#n<_3`WkO5z%oX`wBUgRP`^c4*y^tAsD(T2);3q^DpHm_ z#|ex+K7yjWiRvl1k|J2tt7IsoMU`1X=2R$8IC6vGT{C6pH1ZiT3*EE^L*{k++$)vv zJGFcVcS~>@o?Zsh2oG@4*y%e6%R)#qbt} z4$iEewnzR8m$g*^O(Tog-@ZPrlHlW30$nx%8vR+VJ^+#F>or`rVj7U!-KVwiKbIv| z0U{1hZVA<9i0Zy}Go&->pSM+30U7}T-K2lx-1{)x`7iJUq22rkv6b++(A8(o&a({jlXo&INIUe{~GaT+#|_m zk;wlMxYqe}3105Q(YGu1YaWyOgIBSPk$d+O402|?SHc#7l8FPnMXkTr>o!IO4<9$A|@2OG330*&Kwd_zA zGF$~ek1smmxP_vbF5`=OzKoFb0U8R~NavkDdVhcViz(`0foq!lYnF$!FJ!vPk;S?7 zGaN_b6s>Ho;^>x1a=c&<)oie3&4M%x;@z?TDV&Z%)7?|jO?R8VK@gsVQ`6m%zWxYr zFVS{=m(r5k2>5gupgPfXzo9fzN4n-oaiVzkIQ+yo6*J%|GOQRGmdx(x2Fj3cr#3{3U|C#;&=O>S z;*7^C;0Tc9HpYj<5ZW0)A<|X36fst+ccVVL4KhXsvIQ{WH^GPkH{m#d-op{1bX|Xe zJepP$Y7&^%I&%Cmh?j<_&wvUMQZvAVP{iz7B5}mF+tqIcWfa=d7ST7vO7qp*Aw6gE zbC}*(<|h5mv*ppbz2Dj>$Ra1gn>(yMs<0Sr0K3&Uglhl9csOt(^ zsM^}Vwta-zrc+`ifgg`rQQbjAjW{8V@`t< z80H`5nn&R4>-2|IZKglLtLXsm>N9n8+E5*xR`32HI>)>WCbZ$FxxRD-)TJ%BRXL0y zEx0n{DpriSc)2X=JGnZcqx8HQaCz?nVsd;+OByjwInP1s??4Q$$lvd{%~Of#0@+ztxhPK zep!iKBv{IsM0gF2`M@@e4N>0)I!Z7PvNqsT1t^W@tgZjHw)f7v?Y)b~ZTPc$0sp+b zcs%d+E(Y|I{u$UGJO49qf8VmnY-g>f(~KHEN>Q-s9-p8M95jE)u5FgAo69iCF>U09ESS-?CNCzPoI>un)*NW*@YgyYd^R&go!?|7OU5?FlqP zZ+mYqM>O<$dom`-?(KfN`(o#t-ES3??`<(P@W5bT0!@F%$Qfhq`Q(B&?+7!;ENkR_ z4#)2JJh~8UmYh$lStlg@0O+*l*~>4|L(zJMJ>k>hK-IO&rde+|c2y$Q?TLrVSeCfV2aDPoDa@0-6ypw4GK1M-%`N0=Ak^L2^pE6B$p1Hbq4x z=~Idrk4KS0m-hUs!B#rL>JTFXY=T_694|{~ae#j!v>(XCtsNIXr|=SmNzPXnVT6fjs7UU|m<2+%;`odal5f=7K353r)(A2H337f-yj) zdri|W^;>pO&U>rRNRgfAO}Pfp ziQ<3kK{MUh3krY(wpg-y1Dn!mT)n0(+)Q~{Ii=^hMP%NgEDJlEXDNyqO~;oFOG=|C z51k>QHtp5#CPTumxspt@SCX!kn_VzrAz0B2zvgL&c*kMdt&(&I`#-+*v@4EVT%#s0 zW3Rkq_$=%0`vn=D%AG4a&k#^ET|7#F_UN%?n{FqxE7W+ZTQP&R7T5av{LhU5Ho|<~f z^M-vcjS99Y))7O6RwG5cSXsW5xpBNgYjGa+ec z+Qm*Q^3cNRxWcHWU~^Nl$$5^^O%aJ=byL+rE0~@=uPGUoiW>0wR%WX;R(b)zUZvFf z13h6+6%H9#)h^2)HyCdx^Kks*q2zxHRH{v5dM(p}FO?QSVv=rAemSEUD}~d5-Y&qh zB$GI92Jjk*q@8NUw3)IUTS}^cNN3C z@WGGAWXIrjom>BbnKZCuZ!b3{OeIqkW{Hh+oN3T=8cDmZ0A_l60WT@x^Kv(VKPIUy zFL|?Dag7#`Mjx{GZ(NU`XH-^P4q&2{|ohDef90!TwCostetiJ+fB1k-xE% zA<^(l>1nhH<_CS2j+*CknQ5d#sgmLhsE;jaD#ogz);Rnm&s1TMLNkASL!OdwE;|C) z9F*OaDiRw=bo2(XWZ*?`|s^GRc-1ZW<9S*mX!EvjvCU0rEeOwL9z$$Ql zr7Af`qG=b;W|qp?%jJJqMxj_`J9o(;+BhE~{v49=G~m*z#END)XmcRU5?cVl7+*HM z=t9BFXB!#gggDZ6b{h&a69ELc>dUI)iL990;-wyaLR(2yBR#s~+DB>H;mvS|Od71h z365c)9ZzrmZWWdh8g#CBhf?psTpjFu{mG9@hz5i4EQ#yEP7{Al=X7rZB(gEJGBK&u zDlbkhDfL|D0*}^LQm8;(8&oa zW?aHJxQxUKvFy#s@a*8jcW=&yZ+|8@Z6#+nVPe4lqXN`Vqj-Ta5P(1f&X5Kgx}ZJ@ zWZ_v5Pta*UA`;uZvkK*x5l#ZmLV*1F*g#({OWW}J;HQ71;fI5>H`Ueg7yM)bfMm0( zNKHD2*_wa|lUIoC%ir?^a@#;kzC@E9sfko6e-uF{2C4upeq7T5>b`$}diLYNyElR< z!`vyb`YfHzDZ!<6Gk?2s=I2vVJ8z3yzC3S#4&R3C#CtuDeYQ<$8->Rp)~KJ4-yYN9 z%`pqdB(r~d5DrfGc?%9oq`;p=Fe=8D0jD*?pG=~=1iSwC2rqZF}@F%}0=;=W<{ zpZsuw!=+T6bSfLbCn&eF%{o9XofCI*aC-Xl`wxGNKp_bix&e>4g{d)4oR(nJ&sqMm2t32<^ zb6P51wIVs5WSl8x;KY^P7|>Eezf8qfSS5*<&D$dr!+t4mM@o*O2XaYS=|^zsP}wja-LGWmp@V@vOeAvf<(PZw~)eSA*Q8c}<*p zwMo!M!hZ7M_}#&WUopI&ekE#4^X-#yh&O+S?|(e1z#(&s(mL7EuM;u9t5sixQLrVH z%XhO~RLH8cY3B<>H^fvm5lcrr9t>1U$tcB{j_o}0!#MGB_Z6}cb-U+;9!T5bp%9v+ zetzn=TIqh+x4A2f04-GGa~}>97oLCKyAXC?@e`VMrsProXW*^L>&16yOHfhS`GtSO ztdT`^P)}%cA05GYc;Wb743+fF?|`3rKCsz$;>Q{N7-IV=0 zk1v$pxuu#z^9+tjsi%7M$PK2oZL-o1ZXb!wpD>1b8)DH>Aynl|J?yB*M||w z_A5`@&3^6SFBsU{2wMUNn81tN7le}8hvw-f*^j1c31pi5vGuY2v2%f?K5l=Kt#S)U4{wFF zqslsod%ezM0jl_ZgYH8$6*YtCBB01z`LK?Ok)z zVrE+fe3So}dLvY5Gvn`}&ACvwpX^WbnZTczAX`e1Elp5Mltw?zX4&J9u{X&Uec${C zuE%I#{+<8*k^YJpb?ASGa$s&2)BJ%o@x%q6?XX`RW_(X^94FowV+-(Z8u0cpo*lJ- zpC~Ekku$jf{^RF3-B|h^qVZPZS?n)19CpHJ3MXI7G+R-t9z<9IWgCU;ViO8Xrd})= z@%ofFN<1HCGyGj&m#1lW-(!m&rz2kXA4`s5c^rXcewm~(v8I0_gq?eUf<>&`GSmO2 z?XrXK0uF)m9HdrPxFUcp4rridjdTH-K^(KVmQPP;49vKlr(uAni`&Zxb_X+|AH|Fg z$R{ysP6nDaDR}6Ut6Q*uv%0zf2LU=`J_6jZrDW-A;&;yc27)#>tI!#eYg`!E_m{te zJlTPRUU)ryeAJf%Zvln@Be#lg0pbB67q)i0(Aw?Bt=;|~Yi(R8e+=ozh4TM|w0{=L z{|nOo1-FfH0ayVKYvup7^8Z@-U$pY8SN)&2^l|~Q0s$kprgZ_q0SM2=D1NO%@vpaU zcL8An0mqloc>xp(+rc6JAK*;m=#Q80c>$mSAD57N0p}ObY?*wDQsDSdsN`M7z`W0* zw=uA-KexAzdjUoQ0{^I&)_no~0{_sr)_wsX0TjappnLC+w^o1wX958Mx4wb_OaTFuw+n;;0RjQvmkovi z9)I`fx-Fh2O7k3BR>odxe;o!$ubl#07ca0N9hlHvY$0&;8F|uJ1?~>ZwM~CnNVUG~zfc@g)#P4^|hU(5Oyi{TG zHkXh82p0HQM|heC5eZUS!8!bU(Wj1A!hd{*t0CZho3i`o%;|R3V57e-^qTqIw&Db! zFvDM%N-QN_b(E9v~+jV}0|9*43PXtrW$Mj!|>A$k{57Pv^l!AEY=`DlsKYkp45eKBd5rk*Zf>Mk4qs2>*fO8~n%LXa@rea!}lGI{07D z!5`fn_y_*maqvfX5B`Dw_MAOJvAq|x;djv9b$G#U2G(N*@pli)L!llZfq%Qs4jeK~ zV4E&gi8b|5Lg})4b`YRTwK#iS_>wlB_% zFgSj`6L=`CqicSRI|s%#vQ3*ws(i}^iEYOGZ}(w)0Yr!V7*C=X%@Np31Aotpp*Hq@ z>F_3;(8~{7dWy%4uVG$guS}_NiI`>OhBzXR8*VeSfs`()Cr|sHbS(16W2L6rdio8U zFZ89=y#Uf*Mq8mPh|A8WD3W{3gy%KI4*H(Ct#C-!q$Ks_K&ULo;t%ZY&pZ@$Kaj#i zqx!WNEAtSq)X_vrnh3EHVt@6AjFcREUZDv>NSnlR(_u%QGVL}SPW%AKc9Yw(0{gD0PIm+85}NU z5=`Z$Wk4sacL<2L;&SpKKrMSXy=F2iDc9qaPe<;X=!hbVp#u|D*B;FdrsEI62-YBm z$;JdCll15$&ts?@MS07lobRAwAUO?W3qv~Qr2(o1VDsqpxw(KO8FNzs5|3k4jeF%5 z40>DW^55;r*ZL?**MF37vBVSOn ztX-z#3oax#L zWN4Pevu3(H4~}6n6Xtvw%GosFZq-`+5G1e<;9AGOY(17J=prjXDx(?dIpr0iR0Und zoBMqPNZC)Ede2(nyjI?9;)0Amxk?JZ@VtJlYZYE22c06P$ii9cvWT2_J3vc`za0K- z$~3>*)dCfv_J4Fx@9_~_Hc@$gdn^FV5_hwoGGb3>0a~(@N++0ZI+?#>8QgA}#)p%4 zC}?FD%l!-kUdgtjtM=Gmz#5b;R_O+nKlJd`%he8W3>p_}+cCQ)TSp8X> zf^Q8hYwG#AjdnZZ=k|UVjgvgI-SDKhCetmDWo<`E`G2O%=mKzS`VS92`4w?%>Q4D( z7vCkMx`0TxtI~M~jHgYv1E!O?GNpVJSuHb@wjAz-Np+YsP(NLBe;j@icbjAu=)%n0 zcxAR!K)hxI$XBLB3e!=yX_JBKM1N`6U^cuWDoFlp8T-Y!z1_MEVX2njXzR&=w-Gp; zGGGu$)_(%BgJH#BgFzD3@L4zGJnSbevNd{0l#x#MaE@Hsc7#TZLbfPbS-jK$CFG;n zzr$!Vj3YbmPTrsLD|K-|lbgQlZufr%ko9%^x8mTz>Efa;A7rzITMq8t7)vQzBAc<) zAN%QG+r>vdHqp@#{q5@L6^Wm{eawYMw$U#Y>wnc#O931S{1HVR^s(pX!gdHBnb?mf zAEpvR5nRUR^N5`xoXGGkm3j8&^sMvaq3PUVG&eMXQe>^`D18gCNq#89;|dnX0L~a0 z)DyVGh2$1qm(Ux>c8|;o(){=rUH%a_@0EG zbj1ON%cBQcdhdhi)B@TcKCr7N1Z{cP5spj<%Ro}tFC_K}gU!jr{ut5VB*qn3<#yyPMA3^p6->E|wQ6IEF<@&e&vW+9_9# zjg(GHH_;sv#DOSFzNRvPvjpsElW9R4*)Y;A-`M5>X0MUXT$$@g<=5A9&VrSQC{{3V z$SxRjaTNLLDZ43qn@x6UVo%c>WMFTm<18>(x({2FcvI5Poo0xrcyKsq_xfqO4S(me zR%;)4=9*>2vaisOu(<;p-0#}`>~t<4ZWYd%g|qae#zP&Fc_F>Pxt+nTJSNL6E2KwI z!W|OR{}YpL=TBhqZ5z`Khw|;kCNKprfF=$s4EWcBy)^Z3-VVC1)ET}L*sUJvX{1IW zvk5tWvDnX)N!-vb_6jBi0bjZX=6?rAaoiMRfTE3juaJzl02F4a0wp2BDIzfGZW#*f z2%{cybN2HSdG3uV4vR;FCy zpr*(vljq^+P%H;$41F<`H-CRF-`9;6qe-B>Ps?}mf`Str11pQBWd(zt3-g`4C$Fyu zS)@pGIi*}s6-s(|;Y3lwmB@v%rZAsEumuV^8L1#c3!M0G@D7n)fU;jEAw=UeTK=On zo*co1@?cK|L6+6awz%Mq1Rna5R7W@ARr=EmjWjs`zTYk>f<*6B{ zkJ#$t54`&V7N7WI(AV<7@l7-DgaYr&t1Bp?d)-YX%px@B|6b+kR!=c%1+;83k+&L} zIe2YJyKwQQyk8Vpk^`DPxd&PC!X)Qm@NdzSN8(nbXZ=^Op!Nq$A}FAyS0pkacBX9j zBUo_#!a*EIccjHI-G8h_)MfYW*<_y31fRupJ$}R<5~n zXX@tMrmOVV1j=UXM$Yj#87=rL^I{8Q;np{!T`JP6DI(sZsecARPnyXMyy^?4w%k$3 z^_Fx~PuERYPNM8~9KJz|C7_K$AJ7L$W>_z9Q*J;M2ul)n`|o7mmy>yH$y@|v<0-T* zKXwaA-p9b@xaGipi5!93ZPLSbC3ty)PhZZH3p}L)03NqH;h6m3S(L7`24qIKzET57 z=6@s^eqYA#Yk%-##AHez2{T1c{mg_;u~(AN*=P13dNa=;v%O>8mEl73UXJCdph8D& zJUf!X=%DP_1-1#ioAzMbuY)OwStg9nd}cb*2r7kojSF#;KP@oIi>9M6=lTMOU7GHA zIawUj_yqdwL7l)i}_R!xe$=QE`Ni35RktugY*UD0YWPK6wVxN z{~f)5M@yD;iA7l%bsT*bl$yEg8m0ld{Pkf&4B+sJD)-sVkaVcDi#iE^9RuSvM-Ln* zYAG*ftgvr~Rv+VzH zuizxHTYvP2*wgz%?9%^a2Zy~U7#Gn+m%+GU_f$0VxxMzE-90dKVw^g|?hqSFU6en; z%Cmd**>NzQPsjc=)q>69=rYjez^G``M>jqyYIS+J=on7JR9+&(nLiEate20u>cP+w z^*J?;5MOlh|4c{fJX?|a1Ppbmnb$_qo5F+Wkbp8XUS~~&DInLZ zsA1}Ly92A;Tk~{MOwk{y4#sx0@=t{eSATpSaV8BnzGUtNI~bi;H$BkAvq4X-u}7ZO zE%#83{MFmo?m3!?ghc<}t4yAIXHxq?U?O)}{E0`?8PeCzkjKZS!(c5N2b-RZfdm$( z_<&pc`0)KaHKHpc#uk`TFcKMtL^*^)qS{o6aXU{^<1&E%PwzlLH@b-dXA|S)%YVdJ zTf++jr*eHt{aBu&ybsNHz3!fg*9x#J1bA!)>Fg?i^WG>(@Ja+-%83LEpUfV_=!>0GQ`^%wEjX31)Z|#!at9rdJ}Mj#Qdo2md| z$H3QbN(1GB5{sN)-lD+9C{TD=Zhy_!>+beZA|V5C_}uNfCg=VpXLOK=&Uc~r%vK0Z zJvCb?D?>9O17}YO=^-q>hSEK-O2q>!nRsA!%paJS+5@tA5R`XJf2w}$D6QN zKPH>jdFx_u-fh3U*!G{ruY*ZpShHaVu@Bf3hE|md}0SxyykS&>V3oBO8|E`zFmH{&c zGiYI1e}tC-mjNMvhl9^m`sqw^_}S?>{QJ{wL@D-s^nZ{noi(@V^hR3ooE(XJ>C`&sji0GW+p>zTA@Nzo6%3S~7SG zFmPlx6+>)T3LSRLip3TL+(Q5;3`n3fbO;FTzw@_6ny6!_c8kJk>V1B4#P1H;V3+5Y z0U&=k{tyCd&{WFbZ?KmeR`K=8j@{ZYH{3vZj&gfzVwPS@Gqq!&~t#~3tNV3I3Cg*JBAdx{LCc332L7_2&6a$V;EJyk3tJ`Tn?g%MI1 zQQ&BWq8^pwzCC)Kz0Ed?o`PxcG?A3KOlN>H7)uKZt-6P^4zL1TY}qf?FR+TlsOx_g z4FlQ?S{%cZv#5q{XV41)P1`T7kweBw)i|=2GNWzJL&iylp`%nVSIP9}nuk*hr)cm7 zsUMh?J%?nzFR?85{>syN945*TPK~j5;CLXYvoL-sbRGU{7be-=O-LprGd0qKT&Y?U zNcS0+w6mzw3VhZ&Rw5q@6mTptMq+=|La!^V_;dE$&iOc?%h`0EzHmZs4^^FV)$Q%g z(B85Fd}@sr%K^TFuoP-INQxjSfTReL#fgdWeX_*~?!4Ue*{emKz^8zn&A3pabCWMc z(LS|S339Q?@$`L<&nb*oE2dXp9iYpJkGG4Mh`~k7-tO$)IEX`h(FlpSFCTxPZ&lA6 z1!OcQQeT6>N@fq{Wt67TZ8pgCrcdJRR!$krj;(E{K)GGUNDp!A(cx%m(NQQRg>dF| zv{Z@Io_ew|ep0y>^t!Ls!~Lc{vm%_9P*E$btV&sP8Ds7`e7MD|5i=xXY+Q)i6U;p4 z(pIOtyNMR`cQNXz#z#zKFBpH)_g-{2gDuW%vv4^3|jpT&5 z+-2N54z{8_8$na1_UpdGdo&3qtn7nMvsDF8u}A-=9ZwBA8wPfe5IS;b)|VdCKr_4cwy&$eH~8~@N0oL^9pr6+it*8%F-qm2ibpECA6tz@*s{KA74cx ztX$%_zke3Z=FiZ9_O?$;4rqs83s_##f=irPFO!j7d=t5k?}oplIDoc3a z+$jalrL>keP6-Mo4|HuSV$B{!iADJg1&OpcyT`{cuyyDTJZu~E@XG`x$fIIx}|Vir;F)`PT}fl$&$Dknq$&)f_MF3_wl12cxCFu1Qj%kaH z0?fq)CiJp|1&l)rM^+>}Jmm!AyS&_?h9|c@BcJBZ#GhWz(fhxN7jfcih~_`u`8g$; zZ>HDqeFMJ&_KU{Eytt6ON6*8_c&HRtqhg!;>{)-GaIr3Xl-e+iaO`c6W7Q2Kg4s_Y zKQ~l8wG=hR-gt!%%Nc7>DiyC}h+Cjt2z$?Mu-h=GLV-RHF&r3FT4*^-_dJm(40*)M zMAUS`_bUxL7$0+gi`9xmW`6dVkqRNebm^)~Z6#u47hU}A4doL@6oBKQ6nr2XK>U$8 z9>9N(A@&dZq3f+goYfOp?ADFIsg>5FV%*m;b$gUKZKSE6`QKl87dAN5Ug z0@8Ja7kvH~MimOnWBD1rF-rQ4pGYyNvmT*{D0!ziT4+9bhuPCD5H>^^ULd=o7nJaT z2LhoQ0=tM%6tZonfcLt#oUI#L-6f5{?-YN@^~wlrr@qV3+J1dq3q#MuZlVb}(f>Lf zt9F1=!T)?$j{|gyFod75R|HmF&sUHqH%L>1t(aVB(BnV!>D-0sP3kx100cjT0(h3o zEV4Ng7`iN5;4N_o4bowHJJTe84PL^z=}P=WaM1F=$!N~AKD=;PN8lxCsSL96GNOO1 ztethJ77%;J+hDOm3RBB7H(D8*)`lqsD%xn3F}2Lh60vUaD>dsTFI~^^;33iUItr&a z6&&6(L!NV`^J2sD>cUQC5P8@sa+z{R2#1JKv1`&^EOVzs#^@$W(E5m5tX*H-)rIn4 zZiue#+Lz?buO1YsEX5gNnSg<9x|x4T3}l$-PqXGiHE$>HKj?xXFo#Kl=Ds;S**P+P zqH<>L>4l|3)o&;BaQx#TI@?)(v-@Wf@{U~@qu;wcl$dm9PhkyN@goqi=~eh=b*2sK z%?@7AlSQY~sj@g=xQ#F|I6B9eO#(j&j5|L}$>0E%e!S>lpemw<5h=gHAeYqz_?YTifquyUK4t#D z_#&&sB)tXyJ343*aj0zf%a?aPHf(%P;^-RAi3vY;knWk+$J|r-EHX5n6PZh|Exi0q z{mCYJE|-~w@a=soceXJ0V3zi8ba z$`}&^sph>pSs1K>nw3mZm8FBBcAdhZ0FSH~4H6H@4vVaoY9gzp=P`f1K-e-{KJ%St zo|^qo80b#XvtjsvkB~%?dAqIck*)flS0W552#;ZgDP&#U1tdkk|K6hY?2}d zSH%`D^OKxqnC73cY@CwwK$?zsP?@7${=7d#&?*kkf>u2hqa_xC2ChK+hk#z+759`5 zo7J^U&g8KKnRY=MgAU z)JlhNf(nw^Fu*u33b$mlx`NLAA12L zHpbJF5iF2o7 zSYfURWw(DPv8-i}NwcX7uUv3XhM0tZGKR;O@%V^G_CpqG{g4@wBeE~|Lq@$WUr>3U zgQ=TeUY(vDxF)la#1j;Yrf_iLng=9h8YgJHY&x&cT=O-s+!T%Cfi!e-?_y*_`~j!A zTad-lpksUp{PDs_BZEcO=m^lB01c<@85e#L8s>kOaTiV|MtTDr0M0~)k0C>_+))(8 zR0aNEdY7@b0s_Pe-oerUwm^PAW(Z6rlN9m$zx#2=IP|9*DKBbZ*oBEeEbwfL3UMwX z4x-wE1lWH5K)D~!Y|y**s!iEZo5Ih3KnWnF^03SiKW@ELnPY@k1{}{|;o|aMQpdp$ z2Oodlv3`bGZcNbH&baXt3}K6E;_&)9h_OMUm$HB|VFJ*91awSIQe!+1uz!;|@-@IP zNwMbkO@xFZ@J)Dq1Hf^BhiDK^BseMkx}36bJV+x*qv z9RHpc|2P~|1<2Z9x>7B7QT)d&3`X!POmBGAG>3hldd!EwfI5xuc3yP0X^!Hr-MoLc zZ)hb`yIFN-fP--xgZBr~2gkC+AXA9E?6<|Tr!a=;0wh(a5#iaQgYKr4AfwBXyeJ|$ znZtsJuC6ljnYaz(MQ1ra71`rRAUDQ{nHc#Yp&o_8Z7d9gQWxU_B|jr~Arg_-`?6T? zVKkqxKE8tON2x~iZvyqhmvHQxl&XJ+Qz$3fF~&ieDpAtW(M87^TJn5>!T7?0!h;1A zBey9A`aTR|8c3hr_jimRSQ>UXQID4LIX+y%@4|%WP^P9-3B!amO@Wif;fNgv|kZ&ziktg9{yZ=3`5FhvEAWjTufPseuzRxh(fln0CF_bT+9=dUvD|3IXx2?=r&LG&N z8==o*S|2p_7%>RPg;hrBu(a9SC{N-ZE(MwQjX`*#5zcua(l=)E3*86NzMx{kweT8E zj3D*n2(PCFY~OHf zGUzliHoOhuYq&*)(*%FFq8m6^VMkokm)0(b`=yd7rzJkFcD#TyFc{~o8XwN3 zw>URws@(8Nw+K?!XDF5+l?g0QSllG<hP|>4?+kx{0q^`N zT)n^W8SWf6rpzLx2XH}F#&m~s&pq=!$0c}i+B6xszcX5T4dY;DQaPWDq^UL1Hw9E; z4V7OY5~4AJl#3 zIs8zoX*7}$wy_aH;un(<-x?%{ocA_U*cf;Uvu3rDtn27F2GkwjfT#oULcOfjsGcA` zeV!(#QE>HUnJ+-v!FW_jN<|++W&jl&$e$2D(aHI8zo9Gr0fNkE9Z?XYP$jlnG6Two znv6NydO&~d^MN7oE?^2f{XF95Q&b4`OUPi@L4X?$1rNY={ZU-~5f@xsZQkg&6Mp+X zzO5c>u~NU78ZT1k#bUkF4GnQF%EDlD%!P^n-oKs;^MwfEph{b`%aE{g_(<>sD~y?s z`r_^9LVPpcRkzeTuq*b~8+@ZLolXGbBj9Lrn8*VB0CA_8HyPlWtssTBF5d;}U za=ig!3P0~or^VH5ewNR)B?Tr0P4VybYQKR8-77gDuR(AZdt}bUp*D9{)7fTv21CDE zfuw)8pv&C`g%$iCS^zu{4H%uuZ0G@SOlSglzS}qK^Tnqi-^h%fV|iT~$PF;ZEeIZ0 zC)DbeBsA}6RGTy=vq@uUun;+a#(PQBECJ;-46z||VC6wcC8fovRD*g{G$JARy#lEs zIQitHADsN@grqqnUY8PCCOy>Ai#=<##c~EBUV&00@YK7-OO#2@kRPZPKr%4La5K9q zyqy(CcUDw&_J}Zff+&fIhj%DEnrK7tsrYfad%e@U1|j?iINYq*RJ@3P7M<1pMvgNE=BN2~7s0ecXyS2Y@EqSdTrtM~yno7Fa(iNPjq2b=L|@qK9Y`xn7ANR%W{ z-grA`Pgv$B`E+`>$WaAZ5H{am)y>Ih?9Vi`u(LfR=_6N#_SoNiP!D<3^{_S+Ln$IV_2XdOo zD2YB}`@O!()r_LI_}S}|y-%0`d;QOl*qq~G05rzH4Uxzgh9PnaUiUv+0TYI6EH0NW ztpOx|pA*3DGtEI6PiUJWsukDs@dBny(ayjjR+o~NidXi$%KRC$jW##;4C`-~xo!=Cn6) zz5_Y%r@fqafi`(i&TuIr`(iF|$?gFPYF==-^tG1vCIMqPz5yT3PZa**B2uSU zd)*K0vE2&3odgvPBigrXQ?N7H(h?I#NjDhdG~IoT4d2a~dj#^hwfq3%(TPjnK`H)! z4akhaqwfHY8=T%>9Rvp5jxZO$EH-z7Z`H<_UC(f!Kk5?~eQ%deBDrs*gvZgko z=zY)%W;?v6z=GGk{)2q`>XJ7oomt3fe_%;hbR^}w9EAL9?COgANz0{L$t`Mk@I>Dr z*Aq?%PzB(H(lh`7XY?)ZpI<(?S-}#27t2lY5(^S<{z{pJuvbAGwd^YL0wLk#660zI z@RR{(z&7u9xtLxukV!CZ>!d%y?tUhV19d>GjeBh}hwYdqMJ5V#CJ3O_+9<70$>KISz+ zq643)+kG|ffbtiA#e5BOCvo-h6iYJW|9F8XCy_iLD-%C^{lBed%Zc3V z2!tnC4VhkZb*<=N~VRd~?Mv;w_Yyx<|xb%z`^ckjq0q*f{x`O3? z2BOt!^CQRxijV{Kz}HrRROqFlnKsrI$HY$npRUw8V|HsvL!j9ec7TKXays3wMG@?Y zanV49?b*ao(GEJ>lY}guW4MsbEp`~ z0Mvl!EvYsh{-T=@r*vt5);o-KD1IUNrYFk(DN_y4M=&0F9n2Q%%?yMK5|=*z>a!;e z@8c^dmr>iqKGf&(@HNS6^osxp7rZcZUxELnM$WH_Yk2z=k>5Rla7){{92}ySaglE} zxML(q^hSwPy{%vNy=AplJ__yo1m3bB{}qZX2C7=J#|EDRi$TGEPs^B?l8?7gfEkG- z&z73C;$s^j9TZ!B8?X>RMU0UzZ0Huv?xFP++(-3U%p02Bz(4F;@GX;NpMkNMmBZ&Vk5l{IqSRGqZy8eWAFmpy*+FDY8*$lPVZxrQvnL~CGhnv zIyBe`gTq?m5z*=ogxny3fSVe>vb!sN_4wR^8eGbMR~~$EAC~0-xk_j?>67V%Kczc* zn)C?1#<`w@I`_ILcO_nWns0w3KmQimAVmzcmrojoDZc$25aH(>jk@BOe)HaWdtk5q zxCTu$LB0CRR^YF00mmMEE9+0%vY)z79bvuhCCKM!&H;W=-R75TJf-{P9uF>)Vd@pa zWhx|pKY4wvHxV>mMu+Md`xd&;IO*>^|sQwB|E(aG(Rv+!Di&v{n&eG)r_>|>{ z0N=x~BG}z%R8$m z4vtRRWIHt_ExCf;?6+M$*Eo{zY-4`CV*mXM%f4R!@&y_!TE6>`?6_#~4f3n24I{}t z`hk+Je`Ns%S#-a4)EnHl;RBQ#fdiXgfo@M0E7N~Y z@l==j7WS@=!-L)n_P3+=5kuhuGH2y8G|ni2?@HU!r3-IeGAUhA^95^!sPMB^1!EvP zFNSRZDAwye+C?qoMq@IHrX2S>MKiL{RLG9Fe`X!4k6cHi}`(plv0W_Tjps0b|f+mV$NvS$`z;iSzE`#e8 zr@vTArAgRMD`r8KNfk|Ui89uPq-R|&xlIgB9CZMk5BEfFBaF18 ze?TkMA7h7XwXi0wwe6q~iZObLZg9Zj8_aH^F$4y?5O$V@^01$>-FCJ_;a2`)!R^s3 zxL>RO3sLR?g>lhjaK}%2mSacwKqM)laZZ>`ar~Dc)Pga68i3%-Q9@gs{SPa2Z@mV1 z0|5 zPpB~PDj0@yoaqZ3R=(ur#UGp;tdBjCg0IbR=E!L4e~UXE@c0Ih?Pj(W1`*!fO_zzb z0Uv*b_%N}NU@5G1WzeAk%MoJ?jG(odk=@%~BG&NB4W7&lLAGLSx_LEK3ZFi1n%e?M z!)*7)Kh2#GZP#q4PTn-dlJgH%t5H4EPPmY7kXl%i!8 z90v(EBAp)KByOuKM`)THBKwFrlKIsdGYo&!7H?|CYdNo~8Wd#OZd6E9EHn|@MqI)* z*}h`FW&_$b5AeB*Yci3}S6i~!kUt4|HJ_{k;y`;8NitQ5z$ls>W&y-F;r&wZ5Xrdd z*iAm;SXHvn1Id4bQS;1WjUW#o%-87lB^eLdVGetBwktU12=gEBi#{`bWNFBFcB@}K zT6m-@tf*x8`ErXJMU4SrH=V6>K52ra36B{`0=N-oR+2-AF~Lndn)QZmVPaG@KZ>~L zWeM_^FjHbmbHYgFM*L~dgt%mXA52u;fvL7tBKdLV2>`c0c;a*o2{q_mA^!^gny0(>hQsK4d4}VR11qDBE<8X)oep6 zg&s0$09Fpahd*0Hv_dpMwziFbwzUyZOd9NxTzK}J>UrZai9PIu9c|c<)*p4ynm zu1Mtf+5>5@cILq-h$z>8y%k@fRS5JiR2UlAp@355=rjprUStZhKUG@~^|%5cLePIp+a}|9Ko)*(qi5O6dD}Z$sizeY+VM*I+>!r(a1a?8MWhpmbh&nK zsy6vxs=pP_GxgmioyPKnmTVPBwgM(=Bc@G$KIM=$vx%xF>YM9)*`mvRaHE4X^v4(N zMthRKF3|RJj!TW`f|l2O|5{-JzwJeZHNR`xmlOt>Ky3JU>oIhxc2rnTZ5uk8TYg6g zIvYC{n@ROX9`m|?kJw)>K_iY6HuW+!;{7;Nsi?F7yi%FCu$Zy?eeYpT@4gnz2|~;_;m`n)*cA^|tbQWX1#<=u3n0QfTL&d4hzN3jk_XL(3cnb9;$%1=-4o&N3A|1D?a^?oZJ3CH86rQ^}`07|c;@Y4u44RW+s zZ!GW7#)2&j_L}Mk9E|^wi1+`vCqe>2EtnU+BVBvy>LYn5;A38} z?HVlrS2H{tgNn+doDC~@2kDj(0n~fO=l6^gUt5mNyWFUMqfs&-^eNV~vEI}1qQPSJ zTH%?FDK%cv@(7*xR2Be3Cb_|5b$cKv2yp!Z3hvGq-^w?OjrHO;%TnVC{ckG1LAy!L z*D+&f(ZArs&^$?I-_NV=c{J_ky#cisl z40wEh1lwNZ+WPo#;3R(FP^@6t>Z-f>s@S0rc%{Y=Y-d&yuJ+^58M&TdwhanL-5EGH zM_meo7?FZP*H3bNG)(5%$b(WO3F*@~79waMU}JJBwa%QEgtvncKEBD{w<>$h~ zSew~PN)>|Pv+##LGVL9qR4Kie68ATrP*NY#!uS5M~* zn`2x)xli?MD2Nt>NdLg)kje1>d#+ikjfQ(X4|WA8PtD@*B3Pc#2y^8XF#=O4?+gfk zi5zPS{Mhu*u;JUH|ADTJ79(>KQh@{+z5$GdWLWo%@aO6>H>{Y18z!M7m*3&dHjl?Y78b68RbDlO0H(*R%`Bol|g?Gf*7lR+4|eC^02j;(r%OgQ0%ObMoOT_j9D(gm0z#P4DKk%PvtqwsX9-0iumO zWl&mkF!9F#h6*?E&!uqb45e?$U-#MjxuAFy9HY*@;D>Na0O(|L-%tjXE$AA5EK;ic zIFwe7nB|@_N$u#sH9IK<;Fo)Caz@iMK})<@4K9((z#v>Y72UUnfae(%&d%Sp@&VZy z=$_=ta!6TvG}WgpUvQVqmDPh~k$q2Hb8MB{O)7vW2c}N*aMrrq+o()s0uBGa|0e9+ zlv2Zhu1siWr?P8A*oUa{YAWx4A}0}jpO@Uub%zUyMh-Ge&K8*MPOxX%zwO0qPaZsT z3TRf94u5efoYSM`GDIQQt`EUp)KPNiU`mr=h|#2s5Ww8b5;k(ML1lXLY-dg$KF&3QHV;f*#D}Evsg*KiO<6f# zR)}*#BtA9Q#@Zj%d$Ul66Zp?K!)PY-vz12Q`d=g85CFJ#t=t0NwR~AHqu^wOd!i&WkzGI}qvN!MvJE#UAs?3xI5(jceS_N3o|j_60W=t^J8&}? zKCuFOJ#du(EijOx@T`>;k6pu;uEGHqf3*-2tqIyo(JRxr9ymF5TdhSV;%i=VvkFQL zl2280105^TZA>rm6Bg+F>X?gT01fH9rHtF_1v4qH7(&6x=ZNftawY22iSAdF zVF;SXB&=1KkA!fZFP%J(_oeyI9Jn6}9(g>q`7vm1`5R&KIe{TpkOs;FY0irD?b;cr z`Q~%M@0_^|l&XQe7sWk{ps@6ke{IG7Ook4Zk)6mBkSIH0ivcH*G$e6K7O6%FazEx5 zdR?fKFSc<3nf_`iByeicNh9g(PtmI@iVOWh0Nuzi>EWQ1V$!zrE>=?6Hhf=3!bv;Z z)Ba{d!I3K30dQ7I_}hI&GjVk@r6dp4tt94gNcFmrJJ0a>cpFHKG<@g)f2@3yl2zz>+xB5_fLZ?8K!6BF^o8hMg!y3 zWE77haV`F4DK{Apv8gnRe}**EI26wZsq-GUmqz0L5Z@nUF%0z<6KwLV2i9!UVPZii zixUTGp>-{$JQ$5>5z{yc6_g?kC7FxveL&^l+@3VAP+~(Q<}dz60uD)J!AC8*#Fk;A zu>?D^Vek&;-!_w|d}iQwHl`uPNg{DS60;j-Vkkp3D@1r6QW7Rfe`4H^!%;F6Lme4Y z!*=9`i0!FKPz*9>YGb}mva=3(OXahFLQ~UOQlXQ0M4iH*(}eO+w9T06e@hPskSY?x zk$5ZjdkLxZ@C@XtO|ok3n^b(C#RmO|BS!8zFlVj7R@(GN&Q_OF<8?HqzQ<$HhNfAe z1%un%m$uwn6fn8ZZfRYeo}tR7z|kJx_ticfN2)ZX;RC1;u7k&f+&3xH&y z9C_-QVH~AltVGU+6v=jT4j`Zzkv*9&on;t}&x~Bk)&Qf-mWjs$)v`*q%x$`Bgl!u6 zm-%Y}hGDE;f3|Wl^Jzo6tX5MufZUu zWlo3D0pg2D`AmOQ!e_#H(M=+FeM(3tQb1d9km?D6T<3&lNDKueLPvEW=pULYT1!rG zD5X87WkA_Ru_0lVaZWBmI<%#}RG@e=l2T%5f9LAte{yrjBkTPn^U+Y)!#v(J9fXeb zE0H30wBE>)VwJKW`;kh+D7Cdd(SOV|xGEQRH3)BO%8G3qmsY!Gt;d5g{f(0-eg|u% zVV;JewN?y!Sj#N&YBcX`Et~RybEiYce307a6*VZL8&NnIFmnqnb4oZ42W$%iy3?F3 zyp00Zf2Ai+HgWo?w``ncAt`d8+40s#@q<&#w$3i@X~xokjhzcpO=Pd0SRHrTimOFZ z>PT(rA`5LoEwGNU2gJFE2(!vuZV^()_Tv{RRDyF``baJ2rn&3r%u0Q3+St@fL&?&_ zIQ;C?&5>^6qk5J25XdxR0`maDSe-1Lw|yq;eaLGodS_03(q`B;nfIA zEnBF-&&!G9jqfuX8zoOKp%$UCN-K|>I41EW4#(0yJ|vr$on`*mb@nBvKe|mfsr|7H zf0k3>dx;mIXKh}5SJS9Ykl&<}*lF_HTJXww^xJ;vPqa^rhbVmlltzFtNig33J2&jK{H&CWyqiWNo|&z``3sfApJ$QUh!f&Y^4KnAB@A1L&9xo-tX4n(g`({6DFS036?b{bVZ-BPl5Vw zZ3VQ`3`Q(K#+k6+ge2LNYAv`jZ&#I|u{^^eYJimNgxn#VO|V_k;SmGW#UJ>a*wLG^ z#Jd>upl0w5$QAzVGD>`D%qr@<HJ?XBr`l_PTwc?r$c(>RjB$x!Aaw*+gwV|PlsB~?~Ev$0D{Aj() zMOO}JDM=Hy3`FF7(v?`~uc!WsbeQXVvi^#E;#OcWT!(|PG+j&g!l(iph6pFB(DDL* z69kD0E%Zm$W_d;h*F2?J;6JX*a__v?|Lkj{sJBy;#b?6v`S{!-EtXys$BuTF)F{tp zIT!e8DppR1W^ot%UHz&q@xt#6Jxo^_4a}l1_`7DPP8}HQ(p&3A-eSeb9!5zavioo< zkk~D?SFu{mMkk3f?o@v#D#?6-S%PGL5gA12AxRQRn6OF7S4>7v!hW3+Q&m)!X*KaE zEcw;z%FS494hDW7eTiKXHPD4=Q$=lW@w;&_Hd>AjBvYn^7R{gbu!cS~Iex zZb)F(pRvr+jU1sdwDCAJ>$tam8|1_r|}`P>0mll&dKR95puO zFI>~sW747nyRZ#hwTKh4%G+FjwE4m+a`Ujw7o}1+Re?2@2Q9OOQti+rU6!ljRB3rt z5@M-vPS*ysZiO*3v?}pl=)6^(pEmY79Vm2+Vr`pPCWlmi41)Vcdm`Kb zrXPos&%{dlf+Q?k21oRTTPVR26s_aBJJ(hcaPjM#!ae+x`nhFnMI51}@2p#Ze5Rz}9;M2Wxy_~nw!Mk{6Gv>@Ns>?rGPmP#mZsb)H$#<}(vFTaIRZ3YQ68^U|BC_}2xi8Eez6 z03bh-B>*V|jl&nlL)uD7JS5u~7X#!iMWy=GK1vb?RY%3NRARmv} z!yyAMk+FNwT^&E_RAjKzcq;PC1XQvv*OXNKc@G>NmxrWHK}znwEGAU4ymskX8tYh!e#&2wfOq0F;{$KkMlK0xffoX3?j7u7ti=>I`ll47kQ zpZ=Fa+bKcpSreHKp$S%5&^BsI9ow-m*HcmSwv@*fwMJVVeH$lUUk@S|G-@%DC^L*( z<#Xcg0tBr>rii8^LfbeVWMfZ|x~^x+KPsx3)_G4A78vm#rmv6PKS>lO>6yRUX>IG= zN+OPbJScIyBK)s?71XA4Okam`alB1bpP)2U3q!{oVq3=E!x}GD zw!Mw>(}xViE|uGk5p?gxDy_>0hYo9~RCc!>`B+5vj_fME$ZEo&Fi#Eq0T3k^NjY{T z0y)ybS-F>M(E&CbskHAABe0AAHXlhT7X@}3_1a>UxKckzZLMn~m%Y&e9S|m3s$4M= zg0Vb&YrP`t7dMyc(E&Dps!#sZRyVGW%+rqb*3RPA-Kzr0{NoloGs`M>K02^C_UM4B z$=p7?*(}?PKO4>^tkt=Mpy^=3S|3c{-);$K^Qu(C*_X9u6pri~&b7hRs;+bQcxEmD z-94hZrc`tl!KkI5{&gsonf68?go)SHi!!)>>7>d)XrZc9yR^`MEmSQrq2n1Yr<%2O zX;iXyq2?`N?cVX$uy*d0N?2Q$ZVhX?a`{TBel=Zr-f}BgH*p4E8@`g(#y!s$R#j`f z@VsM{w1x|>8Y8Vm_M!fA*=u#j(~(gFTm7smfXz+VMvUMinMJdr;&tg(RiW$KOH`!z zZMjM|-#I=FjV`KxeG4gVs%mi`n~$U}om_otrgD|(*vR8Y7IoEsO;jxJa}*0yy>&a@%&!tl z1*D=PdwE(($nJMQY*qm}R%LH-pvH{N6?uI_5WCN0Gw;gl+r6Os#UFMqW+`~>_?IeG z9A<1or?k^%Fj67Ax#` z+$m}{9*1Xtl+ER?S#r=GbMd7)qtYL2?py_2=P>ktXChIiqbTwZx^?G11W%%b_43i! zN+a&0<96C`JNDBNFhMYKfJbu6nVXFcCZZ+;XOPqo_P*Dkb?*1~D5dBKpd$%;01I z)DDY(Sc>a1*rX+SYMP)r;=uOZaFm+ZY3pkp*eb71B6KGkI3`^gwW1@~Xz*cgz307^ zwhCKtfz&pVOYE&=bQ(+6fES4gHg^}xNGoM_fWD>_wb=>R`)n|Co$A8G3QKH2TFlF^DlQtiROwET0GTR|w0JCk4w>C(C zi{Ao?Mv=_I6KAFGt#ufgQA~#;RT>?Cgk)UP8;t-BdYgGFuv}p`5mr@Q;F6vXLl%It zC`=D_pLqK_s>E$0XJ@FyGO|b{@KLF4T@DAR(?;vn&^Z*BYjH;g7MiQz1R7@U-VSSQ zI&P#%<@r{I7HY4bZ!YJ-$v-;@n#$a?3AmI{l1E_%!wTt8LP<({PR3W8Y#RxG0X~D zr zui8svo5Y2o9k&>FnAuR3*9;7Qu`W}nE>4c8$_z0$dex2YloUD|`gd4tr7t#iWac?( z16)8P#vX;Otn3Veksyw8{y-yJkwAm(s?Px#A3u2V(7SBXLnhd9DKSttj~0)8>>MNW z+eXVq4L_GEhk~V|xMbNVQbu3}j5>DP7OJ;dy;6%(;gL4>asq}{0(n(`5{;VESXmwJ z)DXJ^a413vGqO`%rvBU<%#a*;-IW0KA&M%Ce}eUtcy`SA}V@%8KCGa{r%if?j{ zg$+f>$5BTcQJpO)QaWv2uddzGlOwpazRn@zLFx_9RXIg18&UaCDN>kQ0B@Hnkab{- zThyhs1P;Zd{8H_Ik=Qs$qp*~t(%mb1^hxJMAKiv(A~V?eEPA&Y?dvX9dJv9R?=agE z2hva&`buAV2v<2r-O)H5=@EMDAD!Toi9Cvl)IlUt$JHnCTCkei1AI5qHV}LCcUzu% zyLJ=JtO=(W#13%?JAyg$;_dtKKCr}~BhAx*kt{42wd`QRM;~GA+t?v$JUAV zL5%x6F+DGTqah<@l6hUNFwwDNns=hZqmjwmbWMh6$g!wHdG38+WimW?$?Fj+sjP_Z zNBd@SGxA#}27_DlU8d?Zp1T-KbCXyWLynK=CHv!}g;|b0s*Uri$|`v~Xe7O1LSFZH zGDco*^Inz01X=XwX>u9`hA~G}eZn;zGKASgtkTVYw1u#<x%`Jie^O!APo}L~ye3oMHYb2M&I#}=L+oB&VX4B0| zB!!|>ng5oOWd5Ij{7?V>AOGuraGr}#XIJwgxGLuP8+<;W&-C|xz1CrmnG>S>AI+Sx+0r{UTZ;H(hUdXpU#uYVxi(5tpMjXcsa@TNl#x1JK(wM`g)Ss-NAZk4F zT2?eeBQ7{clx*X2wVD_C(n*z6N(C-a#Y#5gb|c&B_nyoavt6*BEw>dNH3eF=?TGi- znhGj;`)Qy=ZTk_$sW$~KfC2f|#i>Q42dt1t7Qw20oFI!(EA+ShlGpjFxS3IZ=>d4O zD0%FiY>z5gn9qs>oHgT^6O8hp=K8&Rn<=omd9lftm^Vsv;TkqDI45;K{i+Z+h*fK< z@JH)aQAujDz`DBL1#?7}a#II{=$TD_fw=%Hnm!<%g4OhDfyFkaZ>IC2jBv+?ReSon znr+J4zHM(U$%E($*?wPe^iJJ>Bw50_hDNbCdMm=5Yx%tDN)2FJ6h9s|fQE*zip$lW z5#GZ)+Af@vb5$qRjD4VYqIT@i?-EZD1C?i(jLqL>c!6>D$t!P*E&Mcp9c($57^hV1 z2Vb2(wL{#6+}oK*#^RYA9(bqD%5`AYvDzg7A|}(Ty$yS z`{3SmS|B%(9P3j+>3wpi!5)*rXZGLM@t5$QNM)(1nS zKH}tyBkOFX>Cde!fc){tBGK)0N@^=I+=`38pZ)pM?Q}C+@BZ@VPcK(jZ{W|X-D3Wi N{|_x9Qp?qG2>>M5Ohy0z From 8f25f1413bb34b753f4e9d9f2fa34c50788a37e4 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 11 Feb 2022 12:23:42 -1000 Subject: [PATCH 032/100] Commented out dead code that was causing Intellisense warnings (#289) Also removed some tabs. --- FluidNC/src/Motors/TrinamicSpiDriver.cpp | 9 ++++----- FluidNC/src/Motors/TrinamicUartDriver.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/FluidNC/src/Motors/TrinamicSpiDriver.cpp b/FluidNC/src/Motors/TrinamicSpiDriver.cpp index 79e18fdbb..45ea6ff63 100644 --- a/FluidNC/src/Motors/TrinamicSpiDriver.cpp +++ b/FluidNC/src/Motors/TrinamicSpiDriver.cpp @@ -168,9 +168,8 @@ namespace MotorDrivers { } } - - // The TMCStepper library uses the value 0 to mean 1x microstepping - int usteps = _microsteps == 1 ? 0 : _microsteps; + // The TMCStepper library uses the value 0 to mean 1x microstepping + int usteps = _microsteps == 1 ? 0 : _microsteps; if (tmc2130) { tmc2130->microsteps(usteps); tmc2130->rms_current(run_i_ma, hold_i_percent); @@ -274,8 +273,8 @@ namespace MotorDrivers { // The bit locations differ somewhat between different chips. // The layout is the same for TMC2130 and TMC5160 - TMC2130_n ::DRV_STATUS_t status { 0 }; // a useful struct to access the bits. - status.sr = tmc2130 ? tmc2130->DRV_STATUS() : tmc5160->DRV_STATUS(); + // TMC2130_n ::DRV_STATUS_t status { 0 }; // a useful struct to access the bits. + // status.sr = tmc2130 ? tmc2130->DRV_STATUS() : tmc5160->DRV_STATUS(); // these only report if there is a fault condition // report_open_load(status.ola, status.olb); diff --git a/FluidNC/src/Motors/TrinamicUartDriver.cpp b/FluidNC/src/Motors/TrinamicUartDriver.cpp index 090496d27..4e2313e7e 100644 --- a/FluidNC/src/Motors/TrinamicUartDriver.cpp +++ b/FluidNC/src/Motors/TrinamicUartDriver.cpp @@ -163,9 +163,9 @@ namespace MotorDrivers { } } - // The TMCStepper library uses the value 0 to mean 1x microstepping - int usteps = _microsteps == 1 ? 0 : _microsteps; - if (tmc2208) { + // The TMCStepper library uses the value 0 to mean 1x microstepping + int usteps = _microsteps == 1 ? 0 : _microsteps; + if (tmc2208) { tmc2208->microsteps(usteps); tmc2208->rms_current(run_i_ma, hold_i_percent); } else { @@ -243,8 +243,8 @@ namespace MotorDrivers { << " mm/min SG_Setting:" << constrain(_stallguard, -64, 63)); } - TMC2208_n ::DRV_STATUS_t status { 0 }; // a useful struct to access the bits. - status.sr = tmc2208 ? tmc2208->DRV_STATUS() : tmc2209->DRV_STATUS(); + // TMC2208_n ::DRV_STATUS_t status { 0 }; // a useful struct to access the bits. + // status.sr = tmc2208 ? tmc2208->DRV_STATUS() : tmc2209->DRV_STATUS(); // these only report if there is a fault condition // report_open_load(status.ola, status.olb); From 2538fc24c9969eb5870d8dba83539ca3caa16c74 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 11 Feb 2022 12:25:44 -1000 Subject: [PATCH 033/100] New $Startup/Show AKA $SS command displays startup messages (#288) * New $Startup/Show AKA $SS command displays startup messages * Missing files * log_error() for panic message --- FluidNC/src/Machine/MachineConfig.cpp | 2 +- FluidNC/src/Main.cpp | 2 ++ FluidNC/src/ProcessSettings.cpp | 8 +++++++ FluidNC/src/Serial.cpp | 2 ++ FluidNC/src/StartupLog.cpp | 12 ++++++++++ FluidNC/src/StartupLog.h | 32 +++++++++++++++++++++++++++ FluidNC/src/WebUI/Serial2Socket.cpp | 9 +++----- 7 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 FluidNC/src/StartupLog.cpp create mode 100644 FluidNC/src/StartupLog.h diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index 730564f78..14aafa767 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -133,7 +133,7 @@ namespace Machine { // builtin config. This helps prevent reset loops on bad config files. esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_PANIC) { - log_debug("Skipping configuration file due to panic"); + log_error("Skipping configuration file due to panic"); configOkay = false; } else { configOkay = load(config_filename->get()); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 932b3b1d9..89e08af1e 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -17,6 +17,7 @@ # include "Uart.h" # include "MotionControl.h" # include "Platform.h" +# include "StartupLog.h" # include "WebUI/TelnetServer.h" # include "WebUI/Serial2Socket.h" @@ -124,6 +125,7 @@ void setup() { WebUI::wifi_config.begin(); WebUI::bt_config.begin(); WebUI::inputBuffer.begin(); + allChannels.deregistration(&startupLog); } static void reset_variables() { diff --git a/FluidNC/src/ProcessSettings.cpp b/FluidNC/src/ProcessSettings.cpp index 01d02445a..0151e8227 100644 --- a/FluidNC/src/ProcessSettings.cpp +++ b/FluidNC/src/ProcessSettings.cpp @@ -21,6 +21,7 @@ #include "Uart.h" // Uart0.write() #include "FileStream.h" // FileStream() #include "xmodem.h" // xmodemReceive(), xmodemTransmit() +#include "StartupLog.h" // startupLog #include #include @@ -622,6 +623,11 @@ static Error showChannelInfo(const char* value, WebUI::AuthenticationLevel auth_ return Error::Ok; } +static Error showStartupLog(const char* value, WebUI::AuthenticationLevel auth_level, Channel& out) { + out << startupLog.messages(); + return Error::Ok; +} + // Commands use the same syntax as Settings, but instead of setting or // displaying a persistent value, a command causes some action to occur. // That action could be anything, from displaying a run-time parameter @@ -665,6 +671,8 @@ void make_user_commands() { new UserCommand("N", "GCode/StartupLines", report_startup_lines, notIdleOrAlarm); new UserCommand("RST", "Settings/Restore", restore_settings, notIdleOrAlarm, WA); + new UserCommand("SS", "Startup/Show", showStartupLog, anyState); + new UserCommand("32", "FakeLaserMode", fakeLaserMode, notIdleOrAlarm); }; diff --git a/FluidNC/src/Serial.cpp b/FluidNC/src/Serial.cpp index 4675fa7e7..d89b0bb32 100644 --- a/FluidNC/src/Serial.cpp +++ b/FluidNC/src/Serial.cpp @@ -50,6 +50,7 @@ #include "InputFile.h" #include "WebUI/InputBuffer.h" // XXX could this be a StringStream ? #include "Main.h" // display() +#include "StartupLog.h" // startupLog #include #include @@ -210,6 +211,7 @@ bool is_realtime_command(uint8_t data) { void AllChannels::init() { registration(&Uart0); // USB Serial registration(&WebUI::inputBuffer); // Macros + registration(&startupLog); // USB Serial } void AllChannels::registration(Channel* channel) { diff --git a/FluidNC/src/StartupLog.cpp b/FluidNC/src/StartupLog.cpp new file mode 100644 index 000000000..a6513c4a7 --- /dev/null +++ b/FluidNC/src/StartupLog.cpp @@ -0,0 +1,12 @@ +#include "StartupLog.h" + +size_t StartupLog::write(uint8_t data) { + _messages += (char)data; + return 1; +} +String StartupLog::messages() { + return _messages; +} +StartupLog::~StartupLog() {} + +StartupLog startupLog("Startup Log"); diff --git a/FluidNC/src/StartupLog.h b/FluidNC/src/StartupLog.h new file mode 100644 index 000000000..fd2c67b2c --- /dev/null +++ b/FluidNC/src/StartupLog.h @@ -0,0 +1,32 @@ +// Copyright (c) 2021 - Mitch Bradley +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "Config.h" + +#include "Channel.h" +#include + +class StartupLog : public Channel { +private: + String _messages; + +public: + StartupLog(const char* name) : Channel(name) {} + virtual ~StartupLog(); + + int available(void) override { return 0; } + int read(void) override { return -1; } + size_t readBytes(char* buffer, size_t length) override { return 0; }; + int peek(void) override { return -1; } + size_t write(uint8_t data) override; + void flush() override {} + + Channel* pollLine(char* line) override { return nullptr; } + + int rx_buffer_available() { return 0; } + String messages(); +}; + +extern StartupLog startupLog; diff --git a/FluidNC/src/WebUI/Serial2Socket.cpp b/FluidNC/src/WebUI/Serial2Socket.cpp index 8c717988f..69c00c61e 100644 --- a/FluidNC/src/WebUI/Serial2Socket.cpp +++ b/FluidNC/src/WebUI/Serial2Socket.cpp @@ -70,13 +70,10 @@ namespace WebUI { _lastflush = millis(); } - //send full line - if (_TXbufferSize + size > TXBUFFERSIZE) { - flush(); - } - - //need periodic check to force to flush in case of no end for (int i = 0; i < size; i++) { + if (_TXbufferSize >= TXBUFFERSIZE) { + flush(); + } _TXbuffer[_TXbufferSize] = buffer[i]; _TXbufferSize++; } From 46b8868fbc3a6965e4f328ee8ffa81abd2fd47d4 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 11 Feb 2022 13:43:32 -1000 Subject: [PATCH 034/100] Fixed bug in previous version of index.html.gz with >4 axes (#292) --- FluidNC/data/index.html.gz | Bin 122731 -> 122734 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/FluidNC/data/index.html.gz b/FluidNC/data/index.html.gz index 1885dc6e31c941f3bfa6953f2156cfef1f9cea1e..274369aa7719b7e4ec04bcab50fb0d0de7c7d317 100644 GIT binary patch delta 90399 zcmV)AK*Yc6z6b8U2e5L0e?Vo>(3_SR>ciU_>glZw_4JO0dT?7q9o*Vb2l+!SaG0TP z?h6Z;1-E5o)%nzqBL+DCNpDzs_6dnp)=QR{lC&rl`9^ftyHIR(a-l7fTY=%c#&|w@ zVIaqF?8VkAD-NuNG6F2I57bjxeT6!(SG`7wAzTBJ^B_(j`6WOoe_&MBD+QEu0_5Qp z>8=o-nguNSgL^-1cXryluSx>;6_EHkABnFKiLbvh5?|*d@wJY`*WV8k-{d3l4I=T) zS4QHSd?dcnk@)8OLE?Ts688~_`(GJ}`}s)R*O9p2C=ndfbPEKBRek0Tr3txAD6jdQ zz)_0Ic~l)({|(N?e<2txs~~P>6^cVYirwj;NTax<5|y=!U|!W{<9Tu>9R(Mj^?FUU zdiAD5Ooes7MjO#u36-wpO=j`c%NM^M+y%@Z4KTkk!~BL|ezSJ_6dR$cQcR2hd=(~f3AtZeP!6nLDr%u;ij!< zxvk*33*(5qU`rP(mY8z%}zQhJxezy zJI2(OG1b_Df3X2$eN%N)ZBylJ-E7TlJwvTVwwjW(!La=Gw*OX{jl$vEf9tDn|K-4A z+y7UGt{iZ*{eNZH%0UL(f9p23|12jcV$0NHV6f0%+&sJ^uF!CRyfz;oujv4JUFHBW z3<&wer%&&SPc)3;jd>JrXcTYiU!PIzl%c66-zhp@e;vMZ;IW3BK9FJKVfcZx{04 ziQjB8TT*=Vqt!5tm2k?r$fuCVF@Uicmf$XzMLsv&m)K`f2dv+`OX_)Hzyb_YFuZ-! zJBofJV0z&ZOeuIY#649*C6u`GLF#dV0z?)Ce?MT8#D4D_E(I6z&A$J4_G~2k!L3jhKWwamV928ZM)md*bpt^zg+5nmMSh z2$NS8%rW5xYpGycX&Ue{@uJ8*_xP<+^2M;zEIs&RFZ5w~9q|UTqhHeYh~Fn#X!%ELzw7SZP^k^GtizsAb5dwPEY_-q@W#J+~emKW`nbudj=d zV*}X(o|+-=@rf4&)xQ#NjmJexJH8uxe{SSim#!aEN6eI5#zP}}R*Z>K6a#Hh+#45` z8F{KlNX%e}L1VBQ9M zhhz%9IK1LHl~kar7~?X^4NHeeAypF*8L|@!#bkF3=b3?ODcwx7Rx}4OFAioZe+9F4 zZcM-G3=hVEWWAev1`Rm|8c~Vy*VyJJ?nw$90 zPR5vQ^X5mJmkc;%RBA&_0|?Xf7hb)R-0Y|Z*Jnd_71+59<|z?0v|lZPl>lWSbbb? zx1n^i_3@~^v&o%bY%^C<^mG^>lOgDBe9zUvhsK?z^}gO_-lm%l0ZcE>T3gA>(@tAD zo!*iUPpPnM2-?ABpwu=c#@s!%>D`y5MVK)KI^(oMkAKp;!-7pgZAOXff3a|vFp~VU zcdOdIfU!{IrjQMaLTf&|Dl?h-b}$2g&_$17M-W*#n}V_*EO-Sc8cN3#HAP#}U@En{ zuq~E2=DzIdyV-ia6JP$k+4oMfO^+pEIc{&q+qedurgZw=#z+3Jlk9BA_01i0&5vuF zo`*K9O}0d1hcEkaitgS@pz74G=oXu&URkG`*eA8his1HDZ*60906c6hF%D)&xNX3p!+>GDF^+$G+q^rZ znFt~*>p3T^)WTbQqA;J~NYy7GGBVcGVgmY^?~R8Rvgj~bkwp<{e_S7b(o4gGQHbIv ztrd&7Xk_2;H47K;d1FS2QWgI7a5o^jhl$~^Trf$pF{E4W4ymGwk)6a**p4FMet2Z0tj>3|ZIz!3(~MeSMVI~Ei!%{MT`7~8C#E9f?z zjm+my{tmZ@$1onxru=e($AD4mVlLxyIASfo1Wp+G22=#rDy6A=H=(90#E{^yA1RQnQ@m8)#npuOLgS zGtR(Xc9ozXld=EcWaLiYMfkMrzRP{5jN*6}bvHK7{dhD#U59Ob!=Hri$RBU0!Gh|S zf8Ocb2S|Xy^nicspN^p^`$2wL^rB+HdLvsQe;Unv76Hz3r$g>WZs=L3Zse^?4=+N6 z`tpedC!Xb6EPS~C(Ho*2QiRomGaYz@-HT75`v}l9!x3b`l-UH85A!oe7oyMu^9w%q zVFTAYLEbg_%yc=vnvHy*kBCr~?a}&0f0nR*b;oC8=A93E2kb*h=$edHEZj3@7%DG8 z!+yze3#1Q5ZWu@FL3nNeL@&g~A0c{&2nknnTZ$<&tO+Vwv-}u~Mn0bOG;L7i%p1hx zD@a7x#Psr$-kQzBSs*8rdC&Qa+#H(r14FjH1%VVYy#x-_um_2(a;LOx=&zyLe@k5w zJ>{-+-3!+rlas`Ka++^F5AZ%`X?{FoakKxtj~EgzNLAJglBeXLi}kTUXci5n{E_C` z*~s$Dp_FdYY%u(6{p}qNL}n#|EpH0hoBFV%XV?!H+QRD(9S zByzmgcpvvId(lqMLV;x+^RuRe{XKQ2`Jo-mCqN(Tz!pCmd-(I=)stamRfAx)x=!UQ zy5!1j{(0P>%!#2oUGh;KfA%4^rU3|GgLI1=wWI81yrXXECH8J>OBdk89g_{RJ;@32r!zmN@=m`OCUi5DsdJ|5Eeo zq832aYFax^d(5KXU>wYy$=&jVy!D` zS!`Y%W#{TF(!S|G!7rx57yYjpDcH# z#r))RAg+BiQ8%TrhMjkpjRt?(IW1}LrRactAVeO8{mT_?e}yb7x@Mwo*J0w@;^^Ba zaQrL^zWp04Q=`oL(G-&VL7_;ip2fmT`l2A*e1S0mR%1A1M3k~`v%ZlmoWtCHE0LIi zV-$y3)(=_uhb;V^%fbf1G@F~!7SncN(iMxC#`$tEulpCft&&ludD!P=6~K$aOHs3F zW$s2seay?Te^-)%yIEv7$4)u**|3a=ttE=xuy`B%5b54g^iX#7IGA;tvk!9TAA4u9 zmcWk!CFL}LekNVjN8}U+zz-!7V<{X}l12*1614t9cPmaKhBxCuCD8oip!#u8ecwvk z?8%YM)ekBb;n;9c$ zO0YAIe>eU!$1z3DXcd(hLQzH;#)DJC@n5rMn#+uJ%`}ZYlA2F>ya44eUy66R9h1DA zU8jIS$+0&v?33Btl>Feu^O;F}!1QA2fogs?!b3{e8O7cVm+#cQ@XyIcEafH*3ygiV zTq7S;39GM9oh+t+70kRTrZb!4nzc$ahbgO7eTcLEaZv*(M9JAkl-w$9JGhS?POR? zLf*_t$Ok@%bYxxH@>nb1l>-E4tNkAMQDEO;Dm&vAa=V@880YYPdxxHR>=0#OQj<9T z?Z*2}kQIuD$M=hM4tkwnud~59 z=w7A8`$cf;G=BW(P#GwKHPC&^j)l)$c5p11)Z(JSHpoM;rVt8*00G#>d1@n^f6yRw z-R{uBM9`bo=9v10@z*yk^iQ)1=!Wq_$M6eKM44kKh__kLEUVeF7-n(EAn(9{p(pB% zM2PLi7@>ropg#s9B~;^!PkavweWSMs`AcNtU-$zV zEWMumVp(^v6q~oU6r1W&Y<~Ake-VV(4z;_$sgRSrD81H|sv{qkX51OU&JQqbF9c!h zW)N&KDcuY~8zAT`grI#h2-*cGZ~%bTLI9dK0>Bc!S4B( zzwq43_wm!=FFR2c#1qlrfl7_)Sh3D()|q>pxX!%g@F$sB_c=%S)^Z0~e;0K1g+K6b z9eyybW{dMNlC`<0Bfa527a~RQIv1ED*r1O`JFGfY7-N!EmnA!_>PHLMx1`j_;f+uO zw@+H_TOloPI#gM2{`uXLS9=o<`K_&SXUnnRzxUe~oV)N(jOxejv9m=^1-2l)zJ(St z=*R&70Ee@+jqVV(6ZInPe;nI<3)w~{1Wt3z$42YeZVdlyQKQ%T2| zjHCTCz@CL7J855VwrK9682>#*FVAS<5m-!*XyG%T09%K|Is8LTM8!m6c(94X(_o*VXuVKD1sbanBbTW$I)=$fz}u86zgH8nM5e?1!daS&e1HoMegptM2M z1A|Rdj;YfVz?dPKjwnO=2|WQcWT43|>U%d{zIak-CWV(EG?B$dQf2OoRhq_YZCfKW zZf0!uV0`ReB({>=?@gl3PFZit7MHN!OhYmA!T}LxgLnE*!vnD!c%RWo_WN_9=3aQe zd6xtwKIuz2e>s5M#cJ*B>d$1R9I%GonFsvVz;xf3WJWjJl$3_P!q)UW>FstlD5b%Y z`;*0t79&9Tcx-2xD+M7XSgf=$ByU|NL&;H9NcGffLBPw7~s87jxNV??h7aC(4h9d2Yf9`NdGit;taMwNwrg#*ocH)KO z!Nw1NJ?Z15_W3)ZZ!=v0g<)vNqu|nlEGwAD(7;V6=}rcKZ*ThpI8s7apinMq#B0=N zA;I}=W@R-~0q?uaLuaMfAc_Vr{a5f-lQn8DdIYe{P|?1cjyr9*S=jfX97P-912BW0 zjyvy9e@TIm%{UK(`AqlQqMm?+B6m2%lbZLGc22Y)q>pv!wC&99#s_)no$|(&jdeEC zLzw7L*riOmzb=8YkuF1_YK;OCrxi9ZDn zlXIFoJ9ddds+Ktim~ep+w4=&uaW!SuN+1$lju@9m{R~9yQx}iRLC_Z;X`e<55SB(# za$c%jky4=!iwPr;03iLu@sga%$Qu~?HyC!D<}fdU4nJJ=FFard{P=1qT6*=`LR5rg ze>!ug6tta3K_!M%igFC~3P{I^k8utTwsfBYSY$|U=tv%qhuY$Cx{^;o!sJe;kTFBEG| z5lc(PdJoqTkFkXth{q=h0N=NSoE>MW#hR^FuviVa{2!H&O^b;?W*p9^Lw6dB=A%A~ znQUiaEUW0vwwbS@AECdH&^l>YvX-{A7U~ss2xeh$>fPc{^!DQe#)56nbe?49Wtpp1t&}z zWq&nZ@Ma1$?|O=}N8em>D4-W_e>vg7D`KeVjl{`6FCe9=IUce>=cUT}s}_vhj1SUvRb>|kddl|Iyyf8zNE`Y$gY zJ}oax%8@QDLvoidHxMJ4)1L2Jf;5h@)TV8$S+IYb@%cY~*}B9+Bq549t-sU7DEleD zAVrn@Mae|FWmhFOdp+|9tO_&@Pykb~)S^6cKUl2Vx4NyCe3QxKK^s^(a|f+Tk*R}@ zw7(aZO@kL4^HK!!7~fZXe@p9zh4dnZRi0c#jYZTV5%%}vxM}=xFdvL&*k(L}opS0$ z%W>T&Sl^-sEZM?0p`%6#Ix>v5JV_ZUFQP6JcK_8u6f+FjzA zp&6n(er#xZg!WxNIaC0>&Wppmr+qm^^w)>t)p_>mA^rdcp#>lExsEY@abWAAhdZyn%RFcF7<$5^#H7Tg zlQYrg5<0L}A6&R0k9F7Q{%6pcOV8f0YkD#Y8Ne*pv?yKuV0s?F z>gugHyJzz$dqpav>s7q!rzxOsr5Aaz3z-+(QD-^;U-jO-%0=K0t;R~P=fQ^oBClHW zhm}-lxavB;B(s%F#rtk`-3Q!;zn&aD?Mcj2lywkaf4xb?{w*N+%NmT}N>fUUFU2ahD@D$T9}Ebh5u zk4~P75myiA(G_f*fFxr!k5~uGjyI=mx#T+~^y2Ue8mM*^udSW}D+6olUYxpNrTPhp z6DC(Ve>?12+{+TjI-tdmD?qd`fkeAu4+t^{E8ZGb7ol!fc}S>LZHcd{VX0Oh{1T8` z7}yIkp{exsznWVAtCrjV{BgU*%sS6%v6q!Qo4{y?wz72-K7FeP6-@q|}1liHwHi>sfY&;}hgdoGph*=yb| zuHk!U+;orrK9 z1`$^TnX+ZcnV&&0fxX5%j?tG65b8Q^ptYF(RKHY-HAT`5dkx_3M0*TjyTC+M?Ye{0 zLG>#1>jiGx!h$DyySf&ffD;(Gnlu{(Wdfs{PO{Y!GQ zY1tatSuMoMgH?ns)i47%VM|@fnxYOMbZ3iTw^NNGo_V9Z9gc}Ic+eR0Kjlz{c)b$x zsBC|VFeR%zh*{U_%s$Y64ffZtE;tuOyN8Zgd$o!TI)bcEENl(Yf5@(;5tL9f(PAMT z_Lic5v@a)R}j)EIlqojycnS6!oW_!L1^VnDM(S5WIl)xJYE@lF!{yPn%Ou zyMTJ~2Y6iqN^4$7I7^){ilzkP{;IjU%%2?lDUHUbh1#F2k|pWn+bM_^b5hsR{gf7p z>Dr9*kJzA4tJLD2e+i>hW_JmlRYNK#_9{-VCqEXW`;P{ry>7)b9xvW(2lT5KPyJjUR4s z7WHS?rg$Z7m%7szrb}Muz%DFma&$nt0GvMP)L|J1w}9Mb#F9^>7?YmEl*A*&*su9hrAo8 znx~ust33F!-9@<>q`>5xUsMbcxYcUD0!C9A8&!Hz7HD&7 zjg_7k&H5hZ^if&3S1F}Bf)*;!kXCiguG+OEX#$JMJ@*#rIr4k4n8G&QU`_9}*Qj`Aptu$Wfi6aSU~3Xk zR1SA)e>EF`gQ|LqslY4|4NI%$i^DYyvM9x%g>t)WXd%Mf3)ejsWmO5d8a=!^K*f`Pd!7K{fLH7|UEn{V14+1E5UwF)lt}f64WOvokE=Mpx4Tx}4P*GOFvHM#weA z{aFyky@rzD&#+e{b~rL(rs^j!t>3-Vmrv~4qv!o!pB{ z(GW4MnWR15fBN*{{=vUlMyuwF$9?Fcf4f(AYN%ahAB~#gy&HM3%r+$^Pu-~!V~kS| z!yw`DL~QG>G_&9)D*N-H9~_`5$%lB)Cx+ON(@j?c>UrnKYLQA+ubwTDX~b1BX~8Ph z)x7F1TmAkzyBU2J4g4|eHNcBkfz=ktzqtOiQr!*L`8HK?Ayq9u${VA;of6Sox zEsWWMy>lJ*&i8X~I=JF#F>>Z;w*`H`wu7%wgSn#at$xD%Ubm;?g zg=GdHvDr@^*&{9qhW(OXBb_e>zKPEN9 zbYgylM}WejmN3;PgS#5*f%ID?dO+U2eCzyU;(pK*xScylSWB=pQ!B z*xa86_FLdVwRvUDpw*SVrXJ~QAn2^=hkmtY?s`Ert@rAefh!g=2ZPQXysefaUq6Ly zHoQ6^vwb%V-K*32*_jvG2?qm2jY0YnqE;GI=@oaYFIqL)t@7&~e=6plFo&Es`uthh z!j(CzwY*lUeHu=F``6f0L4$(?7#Mr*_STf2z{Lsn1D)2btp4sx|tzsM$@XL+G>{F6F6c_<%HcGhY5XeOn4o}>3CK~?C z?^Bt2I8@cc;UE6nbs1|kPC9`Dl-`Uzrz_U#r;5t(CsxsWUUx;wsqPVnk)jWZMI}i> z%dDD-?3x6nquJw*3h^~i6;7ySA=4V;I1;U85%m-{e$(nODJTfdekR4V003}Rxu~}BQ%2-+8qwawEiA3P@5`1I;V=7hIyd@fml+TmdO`l;#ZWAZj(-K`I5V{s zhoORhe{+_W+Xy`(VT3cbKw#IJy{AjfowIQ6IKU;)vlsz5QR=0fx+N#paGp5b zEIi%d6p=TyanGn-+H=;oe+y$l zGBB+iUAgv8g8+o$*qcSYPL1CYsfmhOg^OA&SFB!Q7Bkh>^iABU3F%F{(>+3?wlJ7a zhZU#M_&M9mMjA#{UeBD|Wb>+SklwCP6Xssn;`&^A=cb(r-yAc4aBRC`k+04|c3~BT ze*@G}K|e^n{4C*-lq$H2v>atNvzh#62Gh;b6|w@VZX@(2!G%Yw2O~>*KpPcX=v3@l zO1na9dU1Ue39+`K>M2)Q7S)Me6T=kQ3Rb2i5v* z(<@smd{tQZwO;sRVc|EjFl@*DXab93*)cB$sI3!o@rcJnL$P_;tdmn6m5|dXLyHJ^ZzLM4beDGWpD6SLQ6 zLz{=h&gxuG+KUXi^wrAqcgjhdvyd3}Wn0zSVeZA~C2gO*6Ipi$18)}hY-|0z2LBBL z_V3VxzaWdlzth10{{032J9x(aJ$wXzPhOqCzrP)nSvt!8QM7i_#jXmAe>LwTF}c7B z?k&M@UfSIXdc8}rkc?PnhU>6YjHJ~XAPDT+HLQT8Kd5Oud8aUoyJ5GLal5gwGs$? zm_gvfhMkM4(~_8~);bAUe`(MUZWa22d{75vLG3OJUb0IS9EZu_E!AAYwcG+eqKqJU zxs)V%iHFR@4S^~b9WzH%imbAle-nQD`(;;3aa)gn@1OheXnqQX=((Yd3N5_JX36V= zV6wrc^)a7m{#lkhzkB|Pk#-?P|Dff+0xf^n^!3i4oQ7`kzWHxff940V{UElR2!lC9 z_6ue$Nn3wYL25ZUcPY{IR)ykkAn+|(MoO_#{OujFthJ@2(dAZjY9lMXtaIvpEMKpR zk9+Nx(|6P0a#~(@Q#IJnFIF2V?-Y9Wuly~8tv*@k*~hWFfxqnZ>jjqe+>N5kAROAM z#|$j@$@3#Lr%1nSe-N2=`iTR}_ysnDOAxHm4<1}UncD0*YAf0#3mmm%+i+$ zv-D*`Dt%?}DSoE$+0VnLgf@lBem*KSJUOjK1`DlrdiXm#f0;4k%kT84oJKo?0;jR9 z53->St?6v12i0V+TGa8hoXzZ5)G2jp&1y{=63{7j`5WEqb2W|lR3Aa1=?wZp>qoE*}OTdaDhqVF9w?pJt=6 z`r!UKJA?7@e`Rk~9>f!X)_TLZmpN4Cb_W*^fs% ztU6T~W0F;uB|EI@N9$-yDK&CD{sf=78pFh(h#)^I9M-e_#$B~hp z(h3Ztz)quZ*>7^Gs!7UZ>ENcCdHj2(88u01=IkmHoacqBU2(mRtznu@gb@;K(3M??6??sZ3D>d zg&-3MiXoQ{n;wF?BF90`eDp*mfDSEKdv9;g#Ghy*eCh(gBiwaR3%+2HD~^cO=gQni zf5X`v9|A?#5|MMm!31pbi?D`U|8L+=E4FHan=;IkuYTk)#$KoL*8*tYeqmRIe|cT3 zy&d-ujj%ipm}~c%_QeiWY&-QL2Q$P2u1FzC#U*>*TOM>ZyI{NBTPz;{23N8GeE#fZ z-|lcpP=vanXFi5v-t_(4n|t&E;PdB73T$!=jgORShHjCe&>FO?Uz$#~A53oKbe~g2 z=0QC?3{6OH)Y7xVU`=hpL2_nTf5#N~qFSw`0GAJ^Qh=KhF%3aF58c_QPdOg27R=6^ zC9=m~mE>X9NwPts=`#AYOh3-LcTW_4E(~_PbUAvgl@!F7PA^>Y^;|Qck6w)KEeA0Odf7~TgV;pl| zKAA$s1qtVeG|fl)U5730kvkYwa1An3{v+@+UeEL==|7~(w#Jw7uu}qdZCoIT_7F9uAk!X6&(~QZp z!|Qoz9FZqvvN{I@HG0L^e-W4wFC+2|*V8#=ZpwN2A^IyB^_kbiTpf7DIK_FU^u=>t z9$57jz7({IRxgA3XVZUsS1)IHpzxE;X`vghcit7A#2|TPW~jE($TSnV7hXc+X0)Vu zRdf)>Yx6Qag>5KA7Kl)(Fi^m@Wnf}hfe+KFQzmBjn^90rI0~3~f742K>*xc^zn^Rz zLU>w;$Gzg^m*RUCHpdN5a@(_QVs6-O&YfEJN6=IdUICj|GiLN@7eSXP`Wt8D3pKeV zEng_FJX5culecgKV6GY8j1OEy>?D{2$~_5Yy}Z0%NmZ5Yg5A(0JwWpg+oy^iAYXlN5a%*Z@E;qM2QpvI$1#oG`s04n!I5ES>hpPj9n{AG+DJv(S)8{AD(o zLUo*HGggfPiABxWtLD;)Tw>WjL!l}ze1AIkXz=E%?9ZPU-CPl(qO3246mt0ZWS`{K zz@n-XqEBDRe{v1J2CB|{I@C@) zo%^61dUwf;MCl0Kg=1Y~>@^k_*lXhvx;~pnxVM=PSg|*?8`Z)S*j!ntX>vivy~un@ z2oYZm|6*_OWxB`zp1^eU>@^rPj#~zy@l>+>3CQxF%@zvS8KfQy=dJ0C$IOGPG9mf^ z7xKG?fBCDmwpY|5?S}FR`*dqtYoT$!2&cLEY(Dl1^6K?&yPHzpZLQY0 z-zz9^{ho!5xS6PoHUB8+>n%O!FIauYiO*g$xgxQmHxf^x=Ix%>G0_U;fcGe6JPVn9$r<|Vfv5MQ)LjS1ff6Fxc4w-W}DW^D-p{4miHPmCQ4u43- z{@_DtWGs0i#{58+aQ##|J_(Y1KTqRsXUOqSrW~qaL}Tb?f$ttMZXQR?Mh7IWqyUG7 zm7{i~_vN0w5e+w3s^SEUU-__fB$3=F_Iv8vxsqOBHBac=^|^I=mBc<}1XN?*&n>JT zf3B00jG{4#TM1FZR9f>N0MwidRL?xO#k=?`XuI7}0lf2a6gN!lnTq%7gl{a1Oyll^dOP2pB(rNMpB zEC)}f{bF!(z|X>xU@(Jlc^j3?&aP=+;Nf?Bu+~3+UZK!FVj6Q72K2lOg(Ob~Q2SFg z7$AnYfFVywGG8PFfta+|5KUzU<7!v?H0kBFv`m=5{n>1M)lUr%_-r%FSui|Ae^kR3 zHv{Yh*5FWmwNMnE+v>Ghve(=)jDuCrFO8s-TWV$RPD1mdBcIH6D&3xC2#v_fL{B1I zODe!dtVGrz#f72GJ&ja^Gb!IIU;f3&yei7-)itxWCRRasC*MZTZZ<`Lvq*_kOO&0cfY z{54xbk2>`_JrlG>ZdAuRyHj`YPR6E!m3Zz?yB5l(Nry?m^i^a{>Sz;<_W_F`l1Kv% zmGvv@sDYT3{5qWBvWz*>F?XCxzoU!uB;eJmWCsv~{W$3Jq7@slP=kLne^j()S6408 zpC11TRcvl-SNS~g09YI_Vckz31ib(f9}5EmhjAS#UDm)z?RGNhH%F8^90G1cf7SyN zP)HGJQb2`kZR5}4o0XyT0{UF8nO{3iZ+*6ii8_{qnxwBq}FI=ZNfoE^R;L~q(o%F`2a`HNU%%2)ngAa zl@qKLGFkXQzFtL?*6ga)kel~A2kGT^ZN%TT^}~B~T8zB$4A+5jf4j*NtJMrN2rEeC zU(`V3m4!ZGOq^ym>QGuxshN&s6LX9`S$XPqB=fBCVX|-c**2j? zfmvyA^EsWO4MM38e~YV5QInY}oSX%dMnf}tNf|UIrYS#>`B|vwO%gqmao-uZ(`gU` zQ+454tM%2IwfYn6i6a0z2(44t#U?ZKnOVKTX7GIus9OQJdMaF>Ka)7g;rjeJW%T6< zOQ2j><8Z#X(3v~NNU>G2;=IAAJo7Y#Uotk7ya#9%z|rkef6)(aPgC!r*LILX8oO@d zx!px?&^=}qDq4f}7G72SKis#qP#T9e?uUDM0$7(K0x2d2ruJA^va`xcvd{94V1uX% z6I7D9uCE@V8)naRNgzWp)ICtyL zeLo?@KVie%f35mGUva08;aDxM{!bN0+@y*_Qf~2|e##X8=_dvVUOgxC%SBOdh_}I? zcuOlirK$Va5)-j~(~Yj_LYD{N`NUiA`ZYyP?jqS^>)p6UA}z*hByI)%s?0fl4<_d|vFimvJkIGsQtZVrHUi^pi zwo8yv7hheYqK27C@ngGm{^_XLrPEJOt6*fLe;zu6R|%HSl>_q>t9(vF2EjlE4c)C_ zVK!^Yj7g%p#$A`dc`J!S9{fVU zcUr^LI`~M-Fu&CVX@~*4Yb;nv6%^@JTDe&5`ui*M9>`E^*d?n1BHra6IiS21pzMle zf2|5Ve9D2Tih;_RO;fz))C2)kkts3Fyg1A~ZmxR$O~LPRVYnXQi_xKFuNj6*TiLhS z8zh`XVBK`@&uP7vOq;$9C(BygImOD>)1|W#Z8$cq6BgM=IDQBJ zMsK9>upEV}kl3}QZ4i8V9iK6~e~7YX7hK<63cW+Ts|#<2UD5#F47V(V%PruSM~o~E z9{+4k)}dtQjy$%h%zuzf(HH<>K%T!dt>%RDc|{jE4VRy{zJu>Fe}`7+#PAdr1b-nZ- zYvQBh7qs6R!e0Fkhv)jkv1bjvv3rF@#$!Jz(i9B@A-<|ZmJZ60ySaa~y7nPCA)?C- zLxDE0%%eMs+|vMfHg^gII$iszKY#IK4AmGJ@;AHo^DsE2P>zOdhlYn}wL*^~4U3)~ z7}~!Q{~8xQ**-(|1T$e!`{BTfp`8$435s+W&m%`v`F!r>(mdqRaVH$VMw#-FRKoB=3KGo~UoXGRusX3cTi+|o}FlR$c zP3V>^14J>d2WMw|_$?UWk^8}l0NF?o(Tw_i@%#ugVT?yG3Fb@~7x4=WXEPJa;N2F%O<*Q9)cuppn{EXXHl3-Sr(f_#EJKffh$3v9)O zHdWjz_oIf(78+**4;1&LsWSYQyCt$Cd$!$; zbGF$t<;6@fA9n1;7AuWb^Ql)Xaq6K!Wyrj>xOK=ssYD zT!#6*aNT1rz4Tb%2Y-Tw@p(NoR44C@q@1pWZq+&O@Ga$6r?5EY!Dtk7!@|h zXAVIOYpq+!%YSlekj9_-qNoDxH6@TqmOtyYq`?>A!;Whi)NP{A)1r-V_x$2trutZ%T#A`?yrdzJKN7@vvYiNhHDI4s=(;2F&CRE9oo` zdCI_Zuy;+=@MlAlauomyX%H8?w8|8E5Qw!L-;0@eC7 zxJ(?t9$^WnATI4v$&b^|r743uz9u*%laEfA!>doaF+|L~uhD^xc07p_HL;`7)>W~e zLw^ZWXmYnqHS}Ppy#n4*UIF(slT|62(8V}1Bk8pB@zpF-5HICTh1pc6Q%Nh4PGnwo zK4B|e*?En6>n0u7L{D9<-x}+vt8`kUUb;z#!Wdb0_NB{uvu)lD{E*G#Lu}S2+E`Ol zc&|lG_+NH2^dhZ?Wu#M@R?6?seRbC}Q-2H2BYT*U7?z_{`oOO483?7DNz;628%b}G zc7hJOT5-d$D)HGUyPKOOK8*DU6e5C_UQBVafX}Hi$IGHx7{e@KS6L|CC!!T9lQ<$> z@j?9mt%Almjr-_s<-XHs>{ZBgKi%=YdjoXID4MEd3ecUr%oez)5EGaZYBPXSz|`xbgk&oCI)waHmb6 zn0Z8E&+pY5iUn!NOqrD+1dv$qRQrDm>Q}3K@p?2rW$s5SA)ISnEdjO3T!PKMGE@Am z-(VsrpdD+G-L&FXTro$n({!)}XhO&Ewd8$iD!N_iR$gaY`M7*5Z>SYrfPZ5r9fmlJ zo}Ha>&ng)j+eQ*=q@1iFtEpT3t{{NOGWId7kXY=@7Qzcjb) zuGh2w@vFUi8hY-#U6E|PYVe#_nXK0$>vbmU4P_;<<@d1ZH5Jt?Ogx2*7u!=83cV3c zas3g8>Wcw`9$>Gg?ikfCI)5(TV2Jeum!?Vh#c5bin+@xE1JCqqgIaqaDahvJyGHZm zD2td|<-dB3H%NG`i_<}sE53dv$Q5xVIdpj{RPQ@zGK~j=U;X?Uca~nS=L@6eoA2na zUR#ejfXuap+|S+Ef-?|)JN!NxH*<>7vWAiZav41=w_%l5FjkvpA ztOiWT6ZTW6x=*xtb0awyc*C3F@?$AfepE1d?X6(1l0w6RMOs4>booH@ja_rA^!Vl< z|LXm+<6zhl>oK|1parG!&T!mxp&*p{bNY+Jd1zJ^5_TF)Ka*OaVbLNFrmtQLL=`bx zMGI@!LxS3Y|K0ZlVSjlVNB3r#Ei>e{MZvsd9$h@C(>b`=t_fd|Ze4DUjm(|_I2gXQU8c)L~{%(|`F z2XZHs0dOx)`&~l)whbIpDxFtjKub?9jr%?c;QDa6(-$hs@_8-c#Hn~5ai)czGu6LN z90cRoN8dWT8Miq+kM3_*9~`+;_uS(F+jO@xW|vm{a!+xWgPJv}k6U7zSgS_oP^a$j z+)Hw<8%LAZUwE1DV1nPIGEkxVGG;;s=c|Bvu(7A66V zs{rE?F!=9EFTQsVD>BxCUZ`RJu0M@fv|oy@suy5(ej@veu29NgONsl)#0Rd#@l0LD z)jh9kMmkI3HdM7Zbx)^HUYUv0alNZKzke>ohL?{D?SkxjU}ffx`RJA4QNV{hW3ueH zKypce90%v;;{;@K%FbhuvL!DAw)X535EFRj;}_&Bo+>o9S$mdmIL*vgcYikhxMrrEO6u!&1^^N z7s+UFPPs^-ysRpqtv4DAq`;&nd3##aSElqby**-;F{*@msFxZHY|&)_38my43Mj%v zr4sJl;x&PQ!V*wqZVQA$gx?tedw<8jQA5)8Rh;=I1<-9mD{yNu+-7e!5Ey_@g_Xf+ zQd2VZ22*-Gf_>)anCU%2srS&dz_=pFwkrw#mcK}VXQ{DVUS4lUCL5PSUaLr=oyO&U_Lz9Lr+-mvm(FUIu5JvF?vf*rd-v1{q1dP-A(5QN&)?^O_MW}L ztUe+h7``7dvj@C>mgy`I1=<9qlKKM|Sc4>kKg!Wd3l@uN$v8VKk3BeN)yVt53QGi^ z%%uJJuxr!DJUZ>!FX4lGvE{i3`!BGBjCIc}adB+;8c6xxO+_Lgn1A@lg$)wRaR&o$ zM(oO*q8#)YWN8~dLnaE$z)7c2CB4oF(F}+bXxvxoNfozw0<=7!Ms)f7I-Je0*SAmXNA8XtB#kb^{0^3)CnpNKluPx^$KJVvEMG-ws1 z*Sf@DOD{kcPwXXx=6@Tkp&IC;mbPYb&Gb5))i4rru%{T8*+18;pVxm*lVH#0c6Kb4 z+?b1yDQxt;SQ9AXwCKQfD|Fv7eZlFH04F08-) zBAaKeul&O3!3iD389bRS(Mq>uVPPV_Jht;jwit`7X3mXe-pupe{kE{o%*{x_IiI2V zYq^HY%=Q8o!#KC)&4Kqy_swUryCiAy%^$jC`+naQdtJrE2UKhFbr#H&_H++88(=e3 z{6EHz>;|T?iGTUMdz|xBfuOrNJNHGfw`U`ab$1-wU7OVbLUX(c|2NY!Z#TJ8T@oF= z6*H0+oSP{q6y5{pP@bPPzb4};XV36j#1qji7~baLVP5fsC7BW4d-ta5(f|3g8GbYs zz&nnyFE0HEuPj^#a>JJLJ;E2~6x_y5MGE1ud(z2+V1Hbn4C@_?dsL4m@M9X#vvuTM z@qRht{qxISAe|@(yR(2cfnDYHl9LvGFi&V?c2LDjXP54b^@u_28dKaMQ&`^aq36wx z-(5Y#__heF)9XzcL$&9o?~KoaF)-_Oyyk#yI-5;w02?md9ATgp8G}bzH@P1A6rK5W zJsKkOhJR3q-(C7m7tO9QZit8~f`I}Vl3N1FEwmB{WqMPjH-Wg=ct3x}@Y>ULWME@| z;*leNQNAt8x2bg=;sx6@^i<7EIq5@IZ((McGgv@~|B)f#fxT7<<&@bNjCoB)SIL4A z-yRse_XJ@@{bKMMLk2ln3Gs>+bG5Z8)}Z+&|9?ZI$G=H~OwCAWh1G;R9HPr4G?lR0 z+*acHwndo*K(9@-{WiNu2r#`6CuVWI%DzQJokTcckx%4ST@F9w$09SKtSO0}BK)lB z;K!jGjdbwWGO%l8ko$Y=Q~C5^z-CS!MX5{J%wf8gBlJ~`?IsPWG8+7|KnkI$MWe3n z!hf6QN_p#S-DI3e{TP9!Mz_<=X5%fs6z$qa%@zec)x>#VmCo39YD%9+r@)fvIcpvT z6FzFf9#*-Ak-4hbIYvCjJfo^fc?{hLg84!P07{9ux~J!8Gl&7GpjrNIKM1cZ78=N` ziu#S-^9eZvWtp_P#I9krU~EVM%u%Os!&NPP_A&9pfnkqNbnQmI7XUPCQKIL^r3 z7-*|!Z!mdeZ$|1kbF|Yn`<_da(vv*?J+ZRQbv#vOow^r`qA9D7{XC8uHp|C{On=oU zp30i?&NAWcwS8A^ZU$AL#~n52#TG~OTGS5R+?^#Wrc1F+a%q-C+*2m^})Mn<* zrD*Z;Mo6WUvmvJzD~r>byHXD&rnt8WkA;QE!<$%FJUkfjDtTqAMT%JSl(7cof;TB^ z8N_mVWygP4yF`UdU>9u+PzWa3w14<>;O$a`;#R{->yhOFV?KFsc&K62^GMfN{_r*# zOUK2V)K}c4;$?R!yOy^u(Jf@vZH8hS?Iq9#r7` zbMm;r6%YB|t#n(nPs~IJ0Otu`VB5mi($d&XdnR3z0;X!Y1`8Mn;V78;uz!*EgL#Zv zZ5GYBk}9{x;*J@1fK}jRv{ozT)@!LH!j2?rko0E&@YDN5C zvBddv8oVP@oFpjK#-G!Tb3dIH$~5juC*=SnVWsvAi19FbPc}WdZ7^=9q)ObqmOd?| z5aO4KD=Ybj<4Wa@cIPbrn19I9QqAQVv|GC#-ylr;)0(%=-UrCrN)1CX^YC&!ktHmY5dLLEJE_my}+HYf`S>(Zg5IY3=G{zsAHGt!Q_l3b5KOM3(K!wUx-Z6IiL3EGDXI=fqzo(J^oab(pae269~JA2U8q!mi!g}yuPk0TRXmdfOOS+~MM`M|D*_9ML% zrR+&=j5%u4!!n;dKCzAuZ5|EC!UooenJ}*CuEy}nt!@zuCjdJFOMI}&R<@; z?~1`9rl4oD|KdZ7wg8ll<2?=G0344O%_yy6G=}msvncBsr%~o6kv%vAOW%BtC>GQT{uvsbY>Gbvsw+PlvE>|P*QS+7u- zbgEV5!4y4Q#Us{eMV38jYi{-)l$8|@l6EUobz4FM!JKX1oI%`O<9=n@TTS+aG1``zEStNH;n0E(1fIn2ajqq`ovcI~R#?*aiIZ|#hRID&M%99m1* z_Tsk0uz$?}kKPL`gMS%3o;{X-hX+f&l)GW#W-^fvmWQ>d)cD6S&(uiprEXm_a;qO? zeBZJM^`sbvh`E#cca#3fHfPCohO>|IO#(6H@0jS7{*fgXq;M-TtD}}&yEIa*y&K(o zGHTt7em>Q)I~shr_IWnm{Mc4F%ztz?>hLmM4S#DN$-|q`w9>}|0@kjMlWNq#%kk)N z?Ib%ul3N+zNKQ)`ln{(?F@1(f`!O1iuBWrJ=?7h2`?Ta8rLq`CN8mcYZb=&g!|HU@ zFvj(<(B9PR{k$%?#M;FHC6#%%H~x;cI9RTRv%`J|TBT}Te=A*orCDRMW{qL{>@1?1 z^nbT*;ysFjO@knBnSWunGH?!`{MUE?_5Jss9Dn~W-#;BZ`Skl=et7)PpBz;M-D4j< zeDKMCeSg&beK5N!f4N-Fei63tm-4du&U5o0CXVa|O51)yKhT8_jnQ zgVvUO+)Js$bvi9iKY&u}b7lu|wuxj_yLhc_!5>Jh%l99=PxmrpEmx^is2pygPi1P! zwPcbXqPmohJ?IstHfR#Eqt$_K=NE5uAq4eUb=37%nR3m|zR4uXOd6}h=EiHp_kWtV zJyE`NG&9tFg2>{!2?XR~YM(Ow_-g&Nxu|XXR@*K=nJ+8NS=qd#9;)0zizCotY50-R z(CD?+Ym@R;o0Qio*UNmA?@xLuy3|e@C`8Gd0ySg-a#F3x8_2O`IVt>st(>KSxH`iFA8)6HRapTVr-5wW1f_6L~HhmmV z-|zKP+{Y`e=du1^TLx0Pt}c!08#36_gWZ8TY94^_0`Hwp)yIQFnWy^Ig=`O_bLjp% zrA|FzIBwF#7SD^h&Ed^7a(giNz(3q`Ftstb7stD|#9KBsD+*YpUY5l*BY&BV_voIR z7k>F=LJlbpT|d81-4yf3;SAho zd#S7%)?;iRqV>HX_14q2y4qc9>#@68MbFwz{XdUgvJ86i^09UJ;`e(Pj<>=1^Z-R3 zY>t8VmY~h2ttC`1%9s4qW1Q$~Vc6FIAm3jfrFyG3W2}R3dmCuV^)_=^EYI{hvps>B|Ojy6?iO!Sf&;9B+DOk6vNd zuY2K7k->JihJf%Lk!Jeo$LYnZ<*4_f3F+v$?`7Z5I{(H&1b-*#bU;J${uJ3e+c-wH z?O@RB&{p&?$IM8o{L?{OrsxFP^32}U5U3mZ^mS8TvH8Fgj&Ms;d_`A}Y_F26cVo z+)%T6WH9d_>VFv5R$TX$9fgk70SV7kzCFUNMt>mtF;sB-O~>_(of>a2mgQZnkaYL( zxZYQm4f*wEa$sZv^5*SsaMQ1MqYUJS-wB1VuRLjGdYWIAV`nz69QXh=9@S7w?k1pr z5LL~6w%cUp7hgDMxC47!RLst?UvlnsifdXLbX+5DbALW{xVXCy>=PT5sld8IW*}0$ZB}9c{e6uv_pI@!s9>Jb4+!7|xYIx z%Y^-lSz&lV+L28H?sV>&=|E~)Kwdy)0E~kI+yXa+i|PK;YydL}+3!|NDQ~f)uxQRI z+)enG`G4}_i%~T?SsslpW%}7HGG7du zhE_uj)Wj)f+rD@UAitCSb2hZKtSO*Nnr0;oOrcFTEG@7>pc}>asAKwEr}pYEjb7zf z419t<%7ou6hg44Kc)v9a=obpg_K1MpUNpxa-ciQ~yToiul%itcK)t-Xt6~Ne&d!}` z?wA$OTKU@{q=StiPlN?r*$`T|Nl`a8{-jW$_g|29s{!nCd0s=*CK@AvzO+!ZlO5Jx zG~VIieIBKgf8Q?^?ei$%m&u8=BU92pzxYD0^tsQJbd$s1B^cW>C9Nk)x?!Fq*;}~% z98Cn3eS7SE4yBUdI1m0W$)PkPhANSo{$lb}+>>D79g}3>6@O;|y3eb0pI7O3$g6bH z4lTXYrqL*D4sYc){;T!KJRn-|N` z<+T2>x|GwuEPu?;_rvGke)Hn=J8vle`(gO+?H}l5^wV^88uIa7A=Gg(J{|mr=ls8( z^Pk>eh)$M5B&N#HRHXYXYQ8LsO2%|L_|L%*zY$EjI{j|&lWds?(0(WXjpV;q^55Cu zhvCJ1JStCrmbsV}$Aho@bTE{Ys9MQ$;17mx&hQjR$bVrVs1R0Fe&*ibeUm+FRaDP3 z9${KetGBA9l$Z4k538rGBmLOOsb zMVr(wPk#`S-h9^I_dh3!{tlQ|m$ED#BQCuQ{&@aIx*oXQFO&I2@tEn31R~I z(yrj|f**QdeE0IYkaJCjE+D>I7NZ|eSo^c}+F@Vq(U#huuh)+HYQNf2``LQ!xUcqE ztAF-;gWQd6sc4X~+BNp!I;z%@vD!8E;X10;k+Iq}_Tf6J){(K=HTK~;s@kS=W`W~L zqDkNmf94P59MW^gif}L&?@iC~@Zpve3r&Y?9=ecaotI%}mo;r`A!A37r8?L=0Q*bR z1#u04p3Zbf*4owl>TF^=gH=sBTg&Y<|9{avn=WM_1GomB%|%8X4*a7~Q*r2DDJExj zF&iLJMe%mf-Y4cW_J1+@Np>Kat7qIV>b-&vKz|X2p*m;?yIVt=GyZB14s!XwC&e$n z6i-gQUw*+M`!_lr&#uZ6R$O`9Bies+RUs682`ILSnFIr+1cvFw)rCWV90kX@xPMwz zj{JO8RBvR^j_+JtP|yT$czys`v*UGOkYH)E4+n4-Tf=}Y-bR29+@j%EM}Q980@6G5 zPcEjj_VD?8D+)+)m*`Kp*0UdH!s*?ZB6{lfqUJZm3_)@TlT!n=fhO-J6tcOWAeqSN zX)jy+w3P+!;lnkKYq-u_ciFh72YH+L>zd^fWftX9zNuL zXPY=!`^;Ud{#e`AWm|0Ju6aN7i|}6I`EWc0jzud$M>UQd#w?vU&%bZsMndQX6Rv1XcnNs(D<(5@y*%Xij)_Z z^Ob#m(|Wdh!p2!-^Jgqgw?3uye{YSy5xmOfA(1#vi-fc2?slo z_fNlY}z=6}mCN5UXBzAYPJ7sqtYhQW9UOPc?K<=Cf&hx}qbUk;A> z_^{_XEqGgIk*&Cku#*JCgirlkw}_*pqxUYKW!j^_DA zSrnIRy|sJz>kJ-2HB|Bg4JD4#NZ-47dqK{9jt$#Od zz4;hzJ^O}Doz3ss(qCQ-j#UlWZs=roRBJI>l}9quhfqAW2(>}oNC=?Q_BYoem8`cb z1a0&%t%_(?^)cPn>J`#@ErQ1THA$m}=nJaKnDOL!JQ#kqT#nuzyxArRHP`TC-vTUXXC{eJs4Y!zg2}Ijx*al9Q(#LStj!!q4DgGjOd12Q_D{TA}sY|Y8YlF#jbhY9Gte}-Vygyk(85isH zhy@v-a>Sx*W7gHrZ_@Jia%Mpz!n;-Y@V)hiY$=Z>^EXCLn}4sSjq@F|J8Vw1(Rise zV`aj@<68grUG>A`LERLRyjVqKC_;*yS`Qyqw{7-fd%Jz(u}s~t8csQ$gX!tkNtSWU8D$VxZVK+}Iobj>)z>vXb*@5OzP@W#pYN0=1`ar$r%^Gvia#Su{ zDJ(nHdw&E-R%B3`s&AUco;PoHH1@b@Y}m6&qgk3)+FuPZR1Lp|F)42-PkP9;D+nRm z*Zgiy$WD)KGOKtNY6?GnZrnY-WxfQBr!mMc4yumfp*@v9-)cu2uG5XWg_AjXW~lTa zhz{2}{i>+GMWcTg8Z4`J*c(lHNKL4AYlPXg`G4}0ptdQ~&^{HusbTkjAiIxj+BIWU z*@UA{w~r4NjIZL*`O0(t6TS{p;W}Ep3Ia9dzZRELfm`b&>l(Z}9z34PA!x!_5Bq%$ zAB%J#OhU7k<4~6Gu-}REE8lbe8hFmP0PFEhbV1#=eQ!tGxi{#R&RKf%&^SEab)a=T z%zw6(`gya};ayw;RVT-uRFm{d#G-@L8wQE5);-!5p9}4+PQTMAzQM&@ITdd6tK;tm zUu8(l@)iH_@lSw%LjIE-`Q~q6{@VA^;D`4^)8w18Rl7adD&^mp|0eu5<-ZyK&095T z^laDlS<+`ozg?0h(RN9ulYxb_DPs+!?SEo9i{b<#i{OfyiUMWotQiz5I** z@@8(J>t#8db(7zSH=iA7ps%^?z8VXY-QQRY9Y0IObO|hHnv2 zXkPs52#_r;rt4xaiV9U%r`W%`6Mw8#p6f|#l49yn_(BfS`u(s0K=#gV0?a3M7B{E+ zfi!aX^k9vUHl6FB#AT3K)63Jfrk=m}>e%^m#u5L3Mtd=05FohhzBuS$?64nShZ}f3 zJnp=Cd|Dj$f%Q4kc38mLa2@Pu(9X%)K<7UaV@37li*K3w`22+lr{Ab~xPN7Wdpg;K z{GYv3ErFw&1TnY14;xWbdmAx-#^EE_p&X0i0f&?1n+M^_Ss)Q`}^~r_YgCC zUPJ8&P`uOAu1U1vh}KM{edlMLf_)u&aET5)`8$lw-@zWX9efWDPx_esmQaH>i6>L0 zi74TT>c~ms{HHrO*=8Drb$_|nURZzFVMPNw_xOfp)yD(pFN4Q7Fh8y*X6=`h{jC=d z^b2)u_6vv^`r9S#?GkcV6!$*RlCB%^R4EBbRk6LJ{kY9ISWKq9}e^a?!n>=^X=|(l!0pM#0W!_p3f=i6i`cEsgNt zv#V+%GNkqKo~~9`g>zZ19QjIjdjfgqVRv3{ouCKZ_7FSv)t6v*HpW-Iy)2#{tatAX z&7NWt>uuDN=V6ClhJWn|kxg$PLiz{wxKR+^T1@6=V5xm#w*%9Di9{=#Z|!Dc0qzI? z*mU{rd{y15%dJ>>_WX-&NRe6E#7f^XlIO#NTT)V6mi%lyrZxqAzlFI=trZy3KMqykm>-AJ4`@~wayt2)8&H?b$1^)!HpyTR+J|&!!^>0e zB&CK zV?!9)HD-)7q<`7tYG@|>c#5f+txrVD-)(K1Fjj|~1n5i;VlTWw_>GB{dC(tN(+Q=~ zu!;Y8{#U^A_hKOuQ+2XDn$H9`^yA z#G~F-G5U~Iv9t}!!}7pZ9ga3v#mG>)&YzO{NJ+g~KP+1DH1%n5V>*92y3NTagXw3^ z>?zNX?|r-UI&pfII2%Sr$g`%@WlgCgkfyie41awF1!eiH9kC@7-w`p6Os0Y^r>hm6 ze{4hnt@CLcxbCCr(QRmYhae>^Q)djfBmOQLItyW^5XV*0t-QWsx&*%7(dO#uYwboW zX=FQ+htWY_Gs7LdV}*4;Z?3l9U7%aZ64H45<+Xr|+&#stSRORFHHK3P+-vaJRg5i> zk$-D&7C-%YGQV1$BQ^)QCOpq`e8Kp&>&M>0bKJmpTt7OB9XE`R;)?(3n~58vn8#wd z6pM0xN?6VhJ^u3K;lxybPQuk~Y)IPaHQ!o93^+UdXc`U7nkYg7l_AMMPBNApwMe9Z03s*qtl= zNq1;ixDe^JP7ryTaUW7;hRDYZk zy__gNPNZLH+>e}G&=M_#lvq?gW(lx?!Fed<0T)6R`X&YFb+L(?0{v+Y^!vnrEF(bc zLPIG=ojVbM4FYuUW%8V)95lxqI4tJJ1lb7^fwhu<<(HI_K@~FupWrlj9!N77t`CHA zXg<*`5Gf^wrF1Pb5+^b}GKZK({C}zNC)1DSo4h8qgcaL}-#tf>EGPpsApa~_pDk3{ zQ0dm+Y~vwYk?sk}bOfCjW}neos;UBHdHu2vS>kvjSgYZGN0 z?b&q9t_3Lk^X_>jEk^=&2+%?cwn0bb8U#&T;>;z|^riI7@v~-VVUm9;qJOOP(V%Y| zR5TKc_~37pmzk0s@@!giZ}Mf$2fj=a2qO$RHJD-{yl?@m2;JdN7;=pAw{-JdHm5*_ zD&OS4GLM}+L1FnF3b91*&~WI;VlLgpmtiGRsxB@+1jSM)^i!!=N}K4%4@#_pFa#}K zT&<>OxJok-&^g_~eYPw0UlQ5Td<&nE#oI6z<$V8(lv?Pd^!fA^3&{JAs(aCq|8g#9MEx-%x3aF#7>ku4; z-Ni&pp-TT&?QiW$+JDeHMUEE)vhFf=kYJ8Lv_fR~M^1x~uPghv{$k7GFaF4Tyq0n( z=GwpZy{qM9Y9=x@mCQ^Mui%9m4%p89Lp3F8#Fw6j_pBan^l%&h3O2G$s+(rRO z5R!~jEHX}_&n-O1>JAh^flhV653)~Srgi&?KzYEvz?PBFNq=-{q~&ZeHel-KA)nbY zdt`?=31pq(oC~WId%dT0C9f+?txUfRj0csD%#fT%Vy#9?K}h7KT>X9sz93u(;$#|e zDEdOMJca$d^n)C%Ksbp4U-=Xs(mEdC3X~UOu?3No4ncRGzetq?2|>VxhA}vamJX$n zq^>3>2xpl;h<|pJM^)5hn-O-YP!??v`z(#Z*s3@&vWv;#83j24)-gJ_xVWs|4*rJ@ z@_8f+HwB{-L^84*%|^1-rvOzJzq}HX2nEobMCS2R5a0!NepC!)ub2HL@O{DA)v5qN zB!&kT?j-^W(0wK+Beo$wxRxRENCg!OHr-rK@&sFse1A$~bJ$|*;U0>f1-)e$qk8Bq zP}1RRjNFvF76P}&v`&Vu8s!0x)=ye;Y$(79lz*;So&DNw(L8(AP80OWR<+2rfGTSO z%?a>vAS|#9CeN;QbQpRDyaW}-pW!4PR1>#EH0^*JymC!4Sv%SG$lEJMpvW#>Tbt-- z_}@KGet%m?oE@MDB=H{wB_9tE=k|~chSj7+E5ROkg8lFWdk_=sL`?MCLb8iivOf$` z1d#Nj!QtF-cG5orMzWg&;{57!6aG;ajse8^{paR)Ly6Pb+jt(r-}4W-#ie(LB)!`f z_eWXIc3=587;*jwE$8$jY;fsaAQ=qjKSDZ5Vt+@N`7#zMQ~qlyfl?}sAfU@XP#UFF zo~5<&EX_h_2t=4nRe461IWxSo6bQ3Oq}p{ISIS1wCK{A(Y6TSq-IPL2N(EJlFR6gK zEH38P&=}?-VESGbN@4bxx3qfHr5K*y4H9Y=prO-;)(C9D3($q!E2U zyMLSG&9o$%GA)_$Lhnk}pI`tS*F2yQn;|$_wzLiCB%o0vAZAW!doLB9Ot6%y7zr#< zSi#2lZa%+or!(Ccsfb7yIb;oAwc09+$3_+pDVVDo0q?4iEwSu|5-5azE6Dp$u9$?1 zOD1P4e$W8wZ>WA2Zm!xGk$Xawc?qF_27h&rh?23YAJC8#s?0;G;Ol3nsw?5OX$mV} z)j&ZgIETu`Gqw*Xu%PRp0u=%-m34lSRQi|J3-h&VYDfVGf2iBQCurT(^1`R2a_es# z3d_sN4`L^Wvb)xGTM@IWlv^yo*l&$P2;D1}S zxP4RELIV6XJMa5g)93uEx+l;-9D#;HpqUV8xRXHp2(Uw2;U;Z4H=-RJNv~gnE@IUJ zP5iq{(f*ypc|nJh3rP!#7CsJ=ByfTh3N)Jhe3;F`&^NS*Ba`}6HwZX!&>&~pVWtEf zgix4AT23EX!PZ0opKDv<-(B)3gXCt)|1+kg8-4wK!gm!OF z{tgm)o*E+Fx668W+aSrD^+SMtKHdvKbTYR-hY>%P^xcqC}62D5rKAri(DgtDyA5Nak;WZ8w6A1RI?vdv`{AbX0=M7eTDH zbr={!Sgev#+Qiwhz?aaW`5wayBN|`RsB}#Unz^uGP!c+KZNx>xrRimXzTJd&7YhIZ z^I@cNWuL|d3@8lfE@5|daDTqYDKH+fUx4d!{P+>Gj1)nQU;zN2-On)=yb84+H}e

@e7T%SH|zkUM}NI1l}AndOZP9D?@ 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 delta 90400 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 Date: Fri, 4 Feb 2022 15:44:20 +0100 Subject: [PATCH 035/100] python3 generic install scripts --- .../linux-python3/HOWTO-INSTALL.txt | 119 ++++++++++++++++++ .../linux-python3/checksecurity.sh | 19 +++ install_scripts/linux-python3/fluidterm.sh | 8 ++ install_scripts/linux-python3/install-bt.sh | 19 +++ install_scripts/linux-python3/install-fs.sh | 14 +++ install_scripts/linux-python3/install-wifi.sh | 19 +++ install_scripts/linux-python3/tools.sh | 30 +++++ 7 files changed, 228 insertions(+) create mode 100644 install_scripts/linux-python3/HOWTO-INSTALL.txt create mode 100755 install_scripts/linux-python3/checksecurity.sh create mode 100755 install_scripts/linux-python3/fluidterm.sh create mode 100755 install_scripts/linux-python3/install-bt.sh create mode 100755 install_scripts/linux-python3/install-fs.sh create mode 100755 install_scripts/linux-python3/install-wifi.sh create mode 100755 install_scripts/linux-python3/tools.sh diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux-python3/HOWTO-INSTALL.txt new file mode 100644 index 000000000..ea1533442 --- /dev/null +++ b/install_scripts/linux-python3/HOWTO-INSTALL.txt @@ -0,0 +1,119 @@ +## Installing FluidNC on your ESP32 + +_Please note: These instructions apply to the case where you have +downloaded a release bundle from +https://github.com/bdring/FluidNC/releases - a zip file named +fluidnc-vN.N.N-linux-python3.zip . They do not apply to installing from +a source tree. This HOWTO file is present in the source tree so that +it can be packed into a release bundle by automated release scripting, +but it does not tell you how to install from source. For that, visit +https://github.com/bdring/FluidNC/wiki/FluidNC-Compiling#use-vs-code--platformio-to-compile +_ + +These install scripts use Python3 and the Python version of esptool. +https://docs.espressif.com/projects/esptool/en/latest/esp32/ +They are suitable for any system supporting esptool.py +eg. non-intel (arm) linux systems, such as Raspberry PI's, or +older 32-bit intel (x86) based systems + +Most Linux (and other) distributions will already have Python3 +preinstalled. If you have problems, search the web for: +"install python3 on " +replacing with the name of your operating system. + +The script will attempt to install esptool.py if it is not already +present, if this fails please see the esptool documentation and +forums for support. +https://docs.espressif.com/projects/esptool/en/latest/esp32/installation.html +https://www.esp32.com/viewforum.php?f=23 +_ + +Unpack this FluidNC release Zip file. + +Plug your ESP32 into a USB port. + +At a shell prompt, cd to the directory that contains the file +you are reading and run one of the following commands: + +To install the WiFi version: sh install-wifi.sh +To install the Bluetooth version: sh install-bt.sh +To replace the ESP32 local filesystem: sh install-fs.sh + +### Local Filesystem Considerations + +Replacing the local filesystem is only useful for the wifi version. +The Bluetooth version can start with an empty local filesystem. + +The disadvantage of replacing the local filesystem is that it will +overwrite any files that you might already have there, such as FluidNC +config files, WebUI preferences and macros. The advantage is that you +will get the latest version of the index.html.gz file that contains +the WebUI code. Another way to get a new index.html.gz is to upload +it via WebUI from wifi/index.html.gz herein. + +A good approach is to use install-fs only on your first FluidNC +installation, to start from a clean slate. + +### Running Fluidterm + +The FluidNC install scripts run Fluidterm automatically at the end, +but if you want to run it separately, you can type + + sh fluidterm.sh + +or just + + ./fluidterm.sh + +### If Fluidterm Won't Start ... + +Fluidterm is intended to be helpful but it is not absolutely necessary +for using FluidNC. Fluidterm lets you interact directly with FluidNC +over a USB serial port. There are other ways to do that, so if you +have trouble running Fluidterm, you can just ignore it. + +### Alternatives to Fluidterm + +Most GCode sender programs have some way to send commands directly to +FluidNC, but it can sometimes be helpful to bypass the complexity of +a sender and use a more direct path. Fluidterm is one such direct +path, but there are others, typically called "serial terminals". + +For Linux, there are many such programs, such as "screen", "minicom", "cu", +"picocom", and "PuTTY". The one that is most likely to be preinstalled +is named "screen". If screen is not preinstalled, you might be able to +install it with (on Ubuntu or Debian). + + sudo apt update + sudo apt install screen + +To use screen, go to a shell window and type: + + ls /dev/tty* + +That will give you a list of serial ports. You need to find the one +that is connected to FluidNC. It will probably have a name that starts +with "/dev/ttyUSB". Once you have found that name, type + + screen /dev/ttyUSBWHATEVER 115200 + +To exit from screen, type Control-A k + +Search the web for more documentation about screen, or for instructions +for installing it on other versions of Linux. + +### What Can Go Wrong? + +esptool.py is only available on Python3.6 and higher, if your system +does not support that you may be able to use the python2.7 version of +esptool.py by manually installing it first: + pip install esptool.py +The install_* scripts should then be able to complete the firmware install, +but will fail after that when they cannot run fluidterm. See above +for alternatives to fluidterm. + +If Python3.6+ is available on your system, but (for whatever reason) you +use a lower Python version as the system default you should use a +python virtual environment (venv) to do the install without needing to +modify your system-wide python environment: +https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ diff --git a/install_scripts/linux-python3/checksecurity.sh b/install_scripts/linux-python3/checksecurity.sh new file mode 100755 index 000000000..d2f0283f2 --- /dev/null +++ b/install_scripts/linux-python3/checksecurity.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +. ./tools.sh + +esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin + +if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then + echo ******************************************* + echo * Secure boot is enabled on this ESP32 * + echo * Loading FluidNC would probably brick it * + echo * !ABORTED! Read Wiki for more Info * + echo ******************************************* + cmp -l SecurityFuses.bin common/SecurityFusesOK.bin + rm SecurityFuses.bin + exit 1 +fi + +rm SecurityFuses.bin +exit 0 diff --git a/install_scripts/linux-python3/fluidterm.sh b/install_scripts/linux-python3/fluidterm.sh new file mode 100755 index 000000000..242112766 --- /dev/null +++ b/install_scripts/linux-python3/fluidterm.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Install dependencies if needed +python3 -m pip install -q pyserial xmodem + +# Run fluidterm +python3 fluidterm/fluidterm.py $* + diff --git a/install_scripts/linux-python3/install-bt.sh b/install_scripts/linux-python3/install-bt.sh new file mode 100755 index 000000000..c776cdb36 --- /dev/null +++ b/install_scripts/linux-python3/install-bt.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if ! ./checksecurity.sh; then + exit +fi + +. ./tools.sh + +BuildType=bt + +Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootapp="0xe000 common/boot_app0.bin" +Firmware="0x10000 ${BuildType}/firmware.bin" +Partitions="0x8000 ${BuildType}/partitions.bin" + +esptool_write $Bootloader $Bootapp $Firmware $Partitions + +echo Starting fluidterm +sh fluidterm.sh diff --git a/install_scripts/linux-python3/install-fs.sh b/install_scripts/linux-python3/install-fs.sh new file mode 100755 index 000000000..74a1c0453 --- /dev/null +++ b/install_scripts/linux-python3/install-fs.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +if ! ./checksecurity.sh; then + exit +fi + +. ./tools.sh + +LocalFS="0x3d0000 wifi/spiffs.bin" + +esptool_write $LocalFS + +echo Starting fluidterm +sh fluidterm.sh diff --git a/install_scripts/linux-python3/install-wifi.sh b/install_scripts/linux-python3/install-wifi.sh new file mode 100755 index 000000000..1213d0e02 --- /dev/null +++ b/install_scripts/linux-python3/install-wifi.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if ! ./checksecurity.sh; then + exit +fi + +. ./tools.sh + +BuildType=wifi + +Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootapp="0xe000 common/boot_app0.bin" +Firmware="0x10000 ${BuildType}/firmware.bin" +Partitions="0x8000 ${BuildType}/partitions.bin" + +esptool_write $Bootloader $Bootapp $Firmware $Partitions + +echo Starting fluidterm +sh fluidterm.sh diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh new file mode 100755 index 000000000..2e98d6c18 --- /dev/null +++ b/install_scripts/linux-python3/tools.sh @@ -0,0 +1,30 @@ +# Subroutines to call esptool with common arguments + +# Install esptool.py if needed +which esptool.py +if test "$?" != "0"; then + echo esptool.py not found, attempting to install + python3 -m pip install esptool + if test "$?" != "0"; then + echo esptool.py install failed + exit 1 + fi +fi + +EsptoolPath=esptool.py + +BaseArgs="--chip esp32 --baud 230400" + +SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" + +esptool_basic () { + echo echo $EsptoolPath $BaseArgs $* + $EsptoolPath $BaseArgs $BaseArgs $* + if test "$?" != "0"; then + echo esptool.py failed + exit 1 + fi +} +esptool_write () { + esptool_basic $SetupArgs $* +} From a684268e92ae13d1b96e31319fb90497c16452e0 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 4 Feb 2022 15:46:06 +0100 Subject: [PATCH 036/100] permissions as per other installers --- install_scripts/linux-python3/checksecurity.sh | 0 install_scripts/linux-python3/fluidterm.sh | 0 install_scripts/linux-python3/install-bt.sh | 0 install_scripts/linux-python3/install-fs.sh | 0 install_scripts/linux-python3/install-wifi.sh | 0 install_scripts/linux-python3/tools.sh | 0 6 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 install_scripts/linux-python3/checksecurity.sh mode change 100755 => 100644 install_scripts/linux-python3/fluidterm.sh mode change 100755 => 100644 install_scripts/linux-python3/install-bt.sh mode change 100755 => 100644 install_scripts/linux-python3/install-fs.sh mode change 100755 => 100644 install_scripts/linux-python3/install-wifi.sh mode change 100755 => 100644 install_scripts/linux-python3/tools.sh diff --git a/install_scripts/linux-python3/checksecurity.sh b/install_scripts/linux-python3/checksecurity.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/fluidterm.sh b/install_scripts/linux-python3/fluidterm.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/install-bt.sh b/install_scripts/linux-python3/install-bt.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/install-fs.sh b/install_scripts/linux-python3/install-fs.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/install-wifi.sh b/install_scripts/linux-python3/install-wifi.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh old mode 100755 new mode 100644 From 993f043bdb199d92e1b39efecb2d1bc05b99f966 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 4 Feb 2022 21:21:00 +0100 Subject: [PATCH 037/100] check for correct path on older py3 installs --- install_scripts/linux-python3/HOWTO-INSTALL.txt | 13 ++++++++++++- install_scripts/linux-python3/tools.sh | 14 +++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux-python3/HOWTO-INSTALL.txt index ea1533442..983c408e5 100644 --- a/install_scripts/linux-python3/HOWTO-INSTALL.txt +++ b/install_scripts/linux-python3/HOWTO-INSTALL.txt @@ -72,6 +72,17 @@ for using FluidNC. Fluidterm lets you interact directly with FluidNC over a USB serial port. There are other ways to do that, so if you have trouble running Fluidterm, you can just ignore it. +On headless (no GUI installed) machines fluidterm may fail with: + + ModuleNotFoundError: No module named 'tkinter' + +In this case you will need to install the correct system package for tkinter; +- note that trying to install via pip/pypi is usually not sufficient. + + Debian/Raspbian: $ sudo apt install python3-tk + Fedora/RHEL: $ sudo dnf install python3-tkinter +etc.. + ### Alternatives to Fluidterm Most GCode sender programs have some way to send commands directly to @@ -113,7 +124,7 @@ but will fail after that when they cannot run fluidterm. See above for alternatives to fluidterm. If Python3.6+ is available on your system, but (for whatever reason) you -use a lower Python version as the system default you should use a +use a lower Python version as the system default you can use a python virtual environment (venv) to do the install without needing to modify your system-wide python environment: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh index 2e98d6c18..56ba61356 100644 --- a/install_scripts/linux-python3/tools.sh +++ b/install_scripts/linux-python3/tools.sh @@ -1,7 +1,13 @@ # Subroutines to call esptool with common arguments +# Ensure local binary path is in $PATH (pip3 default target path) +echo "$PATH" | grep '$HOME\/\.local\/bin' 2>&1 >/dev/null +if test "$?" != "0"; then + export PATH="$HOME/.local/bin:$PATH" +fi + # Install esptool.py if needed -which esptool.py +which esptool.py 2>&1 >/dev/null if test "$?" != "0"; then echo esptool.py not found, attempting to install python3 -m pip install esptool @@ -9,6 +15,12 @@ if test "$?" != "0"; then echo esptool.py install failed exit 1 fi + which esptool.py 2>&1 >/dev/null + if test "$?" != "0"; then + echo esptool.py claims to have installed successfully, but cannot be found in PATH + echo PATH= $PATH + exit 1 + fi fi EsptoolPath=esptool.py From 47dde125d92cfe9a0bf8e4e15444065d4411476e Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 4 Feb 2022 23:22:45 +0100 Subject: [PATCH 038/100] Document group and other OS considerations --- .../linux-python3/HOWTO-INSTALL.txt | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux-python3/HOWTO-INSTALL.txt index 983c408e5..a381e3b24 100644 --- a/install_scripts/linux-python3/HOWTO-INSTALL.txt +++ b/install_scripts/linux-python3/HOWTO-INSTALL.txt @@ -12,14 +12,15 @@ _ These install scripts use Python3 and the Python version of esptool. https://docs.espressif.com/projects/esptool/en/latest/esp32/ + They are suitable for any system supporting esptool.py -eg. non-intel (arm) linux systems, such as Raspberry PI's, or -older 32-bit intel (x86) based systems +eg. non-intel (arm) linux systems, such as Raspberry PI's or +legacy machines running 32bit distributions. -Most Linux (and other) distributions will already have Python3 +Most Linux distributions will already have Python3 preinstalled. If you have problems, search the web for: "install python3 on " -replacing with the name of your operating system. +replacing with the name and version of your operating system. The script will attempt to install esptool.py if it is not already present, if this fails please see the esptool documentation and @@ -39,6 +40,14 @@ To install the WiFi version: sh install-wifi.sh To install the Bluetooth version: sh install-bt.sh To replace the ESP32 local filesystem: sh install-fs.sh +### Group membership Considerations + +Your user should be a member of the `dialup` group in order to access +the USB serial device, you can use `id -a` to see your current +groups. To add yourself to the 'dialup' group you need to use: + + sudo usermod -a -G dialup + ### Local Filesystem Considerations Replacing the local filesystem is only useful for the wifi version. @@ -128,3 +137,13 @@ use a lower Python version as the system default you can use a python virtual environment (venv) to do the install without needing to modify your system-wide python environment: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ + +### FreeBSD and other *Nix *BSD platforms + +This tool has been used successfully on FreeBSD 13 and should work +on any unix-like platform where Python3 and esptool.py are available. +However this is unsupported; and for advanced users only. +For reference: +FreeBSD(13): + User had to be added to 'dialer' group, and py-tkinter installed from + ports to enable fluidterm. From 8cacae1a0b816cc5e0be0f805bea255862dd1c65 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 5 Feb 2022 15:34:30 +0100 Subject: [PATCH 039/100] add to build script --- build-release.py | 50 +++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/build-release.py b/build-release.py index fc169ed35..0c2e6004c 100644 --- a/build-release.py +++ b/build-release.py @@ -82,31 +82,38 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): if buildEnv(envName, verbose=verbose) != 0: sys.exit(1) -for platform in ['win64', 'macos', 'linux-amd64']: +for platform in ['win64', 'macos', 'linux-amd64', 'linux-python3']: print("Creating zip file for ", platform) terseOSName = { 'win64': 'win', 'linux-amd64': 'linux', + 'linux-python3': 'linux', 'macos': 'macos' } scriptExtension = { 'win64': '.bat', 'linux-amd64': '.sh', + 'linux-python3': '.sh', 'macos': '.sh' } exeExtension = { 'win64': '.exe', 'linux-amd64': '', + 'linux-python3': '', 'macos': '' } + withEsptool = { + 'win64': True, + 'linux-amd64': True, + 'linux-python3': False, + 'macos': True + } zipFileName = os.path.join(relPath, 'fluidnc-' + tag + '-' + platform + '.zip') with ZipFile(zipFileName, 'w') as zipObj: name = 'HOWTO-INSTALL.txt' zipObj.write(os.path.join(sharedPath, platform, name), name) - name = 'README-ESPTOOL.txt' - zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, name)) pioPath = os.path.join('.pio', 'build') @@ -151,22 +158,25 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): zipObj.write(os.path.join('fluidterm', obj), os.path.join(platform, obj)) # Put esptool and related tools in the archive - EsptoolVersion = 'v3.1' - EspRepo = 'https://github.com/espressif/esptool/releases/download/' + EsptoolVersion + '/' - - EspDir = 'esptool-' + EsptoolVersion + '-' + platform - # Download and unzip from ESP repo - ZipFileName = EspDir + '.zip' - if not os.path.isfile(ZipFileName): - with urllib.request.urlopen(EspRepo + ZipFileName) as u: - open(ZipFileName, 'wb').write(u.read()) - for Binary in ['esptool']: - Binary += exeExtension[platform] - sourceFileName = EspDir + '/' + Binary - with ZipFile(ZipFileName, 'r') as zipReader: - destFileName = os.path.join(platform, Binary) - info = ZipInfo(destFileName) - info.external_attr = 0o100755 << 16 - zipObj.writestr(info, zipReader.read(sourceFileName)) + if withEsptool[platform]: + EsptoolVersion = 'v3.1' + EspRepo = 'https://github.com/espressif/esptool/releases/download/' + EsptoolVersion + '/' + name = 'README-ESPTOOL.txt' + zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, name)) + + EspDir = 'esptool-' + EsptoolVersion + '-' + platform + # Download and unzip from ESP repo + ZipFileName = EspDir + '.zip' + if not os.path.isfile(ZipFileName): + with urllib.request.urlopen(EspRepo + ZipFileName) as u: + open(ZipFileName, 'wb').write(u.read()) + for Binary in ['esptool']: + Binary += exeExtension[platform] + sourceFileName = EspDir + '/' + Binary + with ZipFile(ZipFileName, 'r') as zipReader: + destFileName = os.path.join(platform, Binary) + info = ZipInfo(destFileName) + info.external_attr = 0o100755 << 16 + zipObj.writestr(info, zipReader.read(sourceFileName)) sys.exit(0) From 615516e99d8d11ab7368ab226cfa978831976631 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 5 Feb 2022 15:48:52 +0100 Subject: [PATCH 040/100] use pip --user install for esptool.py --- install_scripts/linux-python3/tools.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh index 56ba61356..e25c07362 100644 --- a/install_scripts/linux-python3/tools.sh +++ b/install_scripts/linux-python3/tools.sh @@ -10,7 +10,7 @@ fi which esptool.py 2>&1 >/dev/null if test "$?" != "0"; then echo esptool.py not found, attempting to install - python3 -m pip install esptool + python3 -m pip install --user esptool if test "$?" != "0"; then echo esptool.py install failed exit 1 From f56d4fc0d04fd65cc9b2c92b0f567e3a7eb382f1 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 7 Feb 2022 15:43:59 +0100 Subject: [PATCH 041/100] esptool from source for python installer --- build-release.py | 42 ++++++++++++++++---------- install_scripts/linux-python3/tools.sh | 2 +- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/build-release.py b/build-release.py index 0c2e6004c..c7a1ba660 100644 --- a/build-release.py +++ b/build-release.py @@ -102,7 +102,7 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): 'linux-python3': '', 'macos': '' } - withEsptool = { + withEsptoolBinary = { 'win64': True, 'linux-amd64': True, 'linux-python3': False, @@ -110,13 +110,13 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): } zipFileName = os.path.join(relPath, 'fluidnc-' + tag + '-' + platform + '.zip') - + with ZipFile(zipFileName, 'w') as zipObj: name = 'HOWTO-INSTALL.txt' zipObj.write(os.path.join(sharedPath, platform, name), name) - + pioPath = os.path.join('.pio', 'build') - + # Put bootloader binaries in the archive tools = os.path.join(os.path.expanduser('~'),'.platformio','packages','framework-arduinoespressif32','tools') bootloader = 'bootloader_dio_80m.bin' @@ -140,7 +140,7 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): objPath = os.path.join(pioPath, envName) for obj in ['firmware.bin','partitions.bin']: zipObj.write(os.path.join(objPath, obj), os.path.join(envName, obj)) - + # E.g. macos/install-wifi.sh -> install-wifi.sh copyToZip(zipObj, platform, 'install-' + envName + scriptExtension[platform]) @@ -152,24 +152,32 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): for obj in ['fluidterm.py', 'README.md']: fn = os.path.join('fluidterm', obj) zipObj.write(fn, fn) - + if platform == 'win64': obj = 'fluidterm' + exeExtension[platform] zipObj.write(os.path.join('fluidterm', obj), os.path.join(platform, obj)) + EsptoolVersion = 'v3.1' + # Put esptool and related tools in the archive - if withEsptool[platform]: - EsptoolVersion = 'v3.1' - EspRepo = 'https://github.com/espressif/esptool/releases/download/' + EsptoolVersion + '/' + if withEsptoolBinary[platform]: name = 'README-ESPTOOL.txt' - zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, name)) - + EspRepo = 'https://github.com/espressif/esptool/releases/download/' + EsptoolVersion + '/' EspDir = 'esptool-' + EsptoolVersion + '-' + platform - # Download and unzip from ESP repo - ZipFileName = EspDir + '.zip' - if not os.path.isfile(ZipFileName): - with urllib.request.urlopen(EspRepo + ZipFileName) as u: - open(ZipFileName, 'wb').write(u.read()) + else: + name = 'README-ESPTOOL-SOURCE.txt' + EspRepo = 'https://github.com/espressif/esptool/archive/refs/tags/' + EspDir = EsptoolVersion + + zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, name)) + + # Download and unzip from ESP repo + ZipFileName = EspDir + '.zip' + if not os.path.isfile(ZipFileName): + with urllib.request.urlopen(EspRepo + ZipFileName) as u: + open(ZipFileName, 'wb').write(u.read()) + + if withEsptoolBinary[platform]: for Binary in ['esptool']: Binary += exeExtension[platform] sourceFileName = EspDir + '/' + Binary @@ -178,5 +186,7 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): info = ZipInfo(destFileName) info.external_attr = 0o100755 << 16 zipObj.writestr(info, zipReader.read(sourceFileName)) + else: + zipObj.write(os.path.join(ZipFileName), os.path.join(platform, 'esptool-source.zip')) sys.exit(0) diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh index e25c07362..b6356fb5c 100644 --- a/install_scripts/linux-python3/tools.sh +++ b/install_scripts/linux-python3/tools.sh @@ -10,7 +10,7 @@ fi which esptool.py 2>&1 >/dev/null if test "$?" != "0"; then echo esptool.py not found, attempting to install - python3 -m pip install --user esptool + python3 -m pip install -q linux-python3/esptool-source.zip if test "$?" != "0"; then echo esptool.py install failed exit 1 From affec2e31a54e2e218241f11c4352bf0373b3cac Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Feb 2022 14:46:10 +0100 Subject: [PATCH 042/100] add the readme for the source package --- install_scripts/README-ESPTOOL-SOURCE.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 install_scripts/README-ESPTOOL-SOURCE.txt diff --git a/install_scripts/README-ESPTOOL-SOURCE.txt b/install_scripts/README-ESPTOOL-SOURCE.txt new file mode 100644 index 000000000..7bfa3fa66 --- /dev/null +++ b/install_scripts/README-ESPTOOL-SOURCE.txt @@ -0,0 +1,2 @@ +The esptool source package was downloaded from the Espressif release files at +https://github.com/espressif/esptool . From 3972f4a3a541ba9bf5d5cfa9a88fe2f1318d7fbc Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Feb 2022 16:17:42 +0100 Subject: [PATCH 043/100] add the esptool source zip to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8af844be3..73c73fae7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ pio_machine.h project.checksum version.cpp esptool*.zip +v*.zip /FluidNC/data/config.yaml From d7a913decb2d651337c7ba86095b061d12443920 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 11 Feb 2022 02:31:21 +0100 Subject: [PATCH 044/100] Esptool version readme filename for reference --- build-release.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-release.py b/build-release.py index c7a1ba660..9a2e6ef3d 100644 --- a/build-release.py +++ b/build-release.py @@ -169,7 +169,8 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): EspRepo = 'https://github.com/espressif/esptool/archive/refs/tags/' EspDir = EsptoolVersion - zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, name)) + zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, + name.replace('.txt', '-' + EsptoolVersion + '.txt'))) # Download and unzip from ESP repo ZipFileName = EspDir + '.zip' From e811d87c05c187ece908f05dc37f29fc0cedafe2 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 14 Feb 2022 11:57:30 -1000 Subject: [PATCH 045/100] Getting close --- build-release.py | 55 +++++----- fluidterm/fluidterm.py | 64 +++++++---- .../linux-python3/checksecurity.sh | 19 ---- install_scripts/linux-python3/fluidterm.sh | 8 -- install_scripts/linux-python3/install-bt.sh | 19 ---- install_scripts/linux-python3/install-fs.sh | 14 --- install_scripts/linux-python3/install-wifi.sh | 19 ---- install_scripts/linux-python3/tools.sh | 42 -------- .../HOWTO-INSTALL.txt | 73 ++++++------- install_scripts/linux/checksecurity.sh | 5 + install_scripts/linux/erase.sh | 7 ++ install_scripts/linux/fluidterm.sh | 5 + install_scripts/linux/install-bt.sh | 9 ++ install_scripts/linux/install-fs.sh | 10 ++ install_scripts/linux/install-wifi.sh | 9 ++ install_scripts/linux/tools.sh | 100 ++++++++++++++++++ install_scripts/macos/checksecurity.sh | 21 +--- install_scripts/macos/erase.sh | 5 + install_scripts/macos/install-bt.sh | 17 +-- install_scripts/macos/install-fs.sh | 10 +- install_scripts/macos/install-wifi.sh | 17 +-- install_scripts/macos/tools.sh | 43 +++++++- install_scripts/win64/checksecurity.bat | 21 ++-- install_scripts/win64/erase.bat | 16 +++ 24 files changed, 333 insertions(+), 275 deletions(-) delete mode 100644 install_scripts/linux-python3/checksecurity.sh delete mode 100644 install_scripts/linux-python3/fluidterm.sh delete mode 100644 install_scripts/linux-python3/install-bt.sh delete mode 100644 install_scripts/linux-python3/install-fs.sh delete mode 100644 install_scripts/linux-python3/install-wifi.sh delete mode 100644 install_scripts/linux-python3/tools.sh rename install_scripts/{linux-python3 => linux}/HOWTO-INSTALL.txt (69%) create mode 100644 install_scripts/linux/checksecurity.sh create mode 100644 install_scripts/linux/erase.sh create mode 100644 install_scripts/linux/fluidterm.sh create mode 100644 install_scripts/linux/install-bt.sh create mode 100644 install_scripts/linux/install-fs.sh create mode 100644 install_scripts/linux/install-wifi.sh create mode 100644 install_scripts/linux/tools.sh create mode 100644 install_scripts/macos/erase.sh create mode 100644 install_scripts/win64/erase.bat diff --git a/build-release.py b/build-release.py index 9a2e6ef3d..728cc6042 100644 --- a/build-release.py +++ b/build-release.py @@ -58,11 +58,11 @@ def buildFs(pioEnv, verbose=verbose, extraArgs=None): sharedPath = 'install_scripts' -def copyToZip(zipObj, platform, destPath, mode=0o100755): - sourcePath = os.path.join(sharedPath, platform, destPath) +def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): + sourcePath = os.path.join(sharedPath, platform, fileName) with open(sourcePath, 'r') as f: bytes = f.read() - info = ZipInfo.from_file(sourcePath, destPath) + info = ZipInfo.from_file(sourcePath, os.path.join(destPath, fileName)) info.external_attr = mode << 16 zipObj.writestr(info, bytes) @@ -82,49 +82,46 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): if buildEnv(envName, verbose=verbose) != 0: sys.exit(1) -for platform in ['win64', 'macos', 'linux-amd64', 'linux-python3']: +for platform in ['win64', 'macos', 'linux']: print("Creating zip file for ", platform) terseOSName = { 'win64': 'win', - 'linux-amd64': 'linux', - 'linux-python3': 'linux', + 'linux': 'linux', 'macos': 'macos' } scriptExtension = { 'win64': '.bat', - 'linux-amd64': '.sh', - 'linux-python3': '.sh', + 'linux': '.sh', 'macos': '.sh' } exeExtension = { 'win64': '.exe', - 'linux-amd64': '', - 'linux-python3': '', + 'linux': '', 'macos': '' } withEsptoolBinary = { 'win64': True, - 'linux-amd64': True, - 'linux-python3': False, + 'linux': False, 'macos': True } - zipFileName = os.path.join(relPath, 'fluidnc-' + tag + '-' + platform + '.zip') + zipDirName = os.path.join('fluidnc-' + tag + '-' + platform) + zipFileName = os.path.join(relPath, zipDirName + '.zip') with ZipFile(zipFileName, 'w') as zipObj: name = 'HOWTO-INSTALL.txt' - zipObj.write(os.path.join(sharedPath, platform, name), name) + zipObj.write(os.path.join(sharedPath, platform, name), os.path.join(zipDirName, name)) pioPath = os.path.join('.pio', 'build') # Put bootloader binaries in the archive tools = os.path.join(os.path.expanduser('~'),'.platformio','packages','framework-arduinoespressif32','tools') bootloader = 'bootloader_dio_80m.bin' - zipObj.write(os.path.join(tools, 'sdk', 'bin', bootloader), os.path.join('common', bootloader)) + zipObj.write(os.path.join(tools, 'sdk', 'bin', bootloader), os.path.join(zipDirName, 'common', bootloader)) bootapp = 'boot_app0.bin'; - zipObj.write(os.path.join(tools, "partitions", bootapp), os.path.join('common', bootapp)) - secFuses = 'SecurityFusesOK.bin'; - zipObj.write(os.path.join(sharedPath, secFuses), os.path.join('common', secFuses)) + zipObj.write(os.path.join(tools, "partitions", bootapp), os.path.join(zipDirName, 'common', bootapp)) + for secFuses in ['SecurityFusesOK.bin', 'SecurityFusesOK0.bin']: + zipObj.write(os.path.join(sharedPath, secFuses), os.path.join(zipDirName, 'common', secFuses)) # Put FluidNC binaries, partition maps, and installers in the archive for envName in ['wifi','bt']: @@ -133,29 +130,29 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): # bt does not need a spiffs.bin because there is no use for index.html.gz if envName == 'wifi': name = 'spiffs.bin' - zipObj.write(os.path.join(pioPath, envName, name), os.path.join(envName, name)) + zipObj.write(os.path.join(pioPath, envName, name), os.path.join(zipDirName, envName, name)) name = 'index.html.gz' - zipObj.write(os.path.join('FluidNC', 'data', name), os.path.join(envName, name)) + zipObj.write(os.path.join('FluidNC', 'data', name), os.path.join(zipDirName, envName, name)) objPath = os.path.join(pioPath, envName) for obj in ['firmware.bin','partitions.bin']: - zipObj.write(os.path.join(objPath, obj), os.path.join(envName, obj)) + zipObj.write(os.path.join(objPath, obj), os.path.join(zipDirName, envName, obj)) # E.g. macos/install-wifi.sh -> install-wifi.sh - copyToZip(zipObj, platform, 'install-' + envName + scriptExtension[platform]) + copyToZip(zipObj, platform, 'install-' + envName + scriptExtension[platform], zipDirName) - for script in ['install-fs', 'fluidterm', 'checksecurity', 'tools', ]: + for script in ['install-fs', 'fluidterm', 'checksecurity', 'erase', 'tools']: # E.g. macos/fluidterm.sh -> fluidterm.sh - copyToZip(zipObj, platform, script + scriptExtension[platform]) + copyToZip(zipObj, platform, script + scriptExtension[platform], zipDirName) # Put the fluidterm code in the archive for obj in ['fluidterm.py', 'README.md']: fn = os.path.join('fluidterm', obj) - zipObj.write(fn, fn) + zipObj.write(fn, os.path.join(zipDirName, fn)) if platform == 'win64': obj = 'fluidterm' + exeExtension[platform] - zipObj.write(os.path.join('fluidterm', obj), os.path.join(platform, obj)) + zipObj.write(os.path.join('fluidterm', obj), os.path.join(zipDirName, platform, obj)) EsptoolVersion = 'v3.1' @@ -169,7 +166,7 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): EspRepo = 'https://github.com/espressif/esptool/archive/refs/tags/' EspDir = EsptoolVersion - zipObj.write(os.path.join(sharedPath, name), os.path.join(platform, + zipObj.write(os.path.join(sharedPath, name), os.path.join(zipDirName, platform, name.replace('.txt', '-' + EsptoolVersion + '.txt'))) # Download and unzip from ESP repo @@ -183,11 +180,11 @@ def copyToZip(zipObj, platform, destPath, mode=0o100755): Binary += exeExtension[platform] sourceFileName = EspDir + '/' + Binary with ZipFile(ZipFileName, 'r') as zipReader: - destFileName = os.path.join(platform, Binary) + destFileName = os.path.join(zipDirName, platform, Binary) info = ZipInfo(destFileName) info.external_attr = 0o100755 << 16 zipObj.writestr(info, zipReader.read(sourceFileName)) else: - zipObj.write(os.path.join(ZipFileName), os.path.join(platform, 'esptool-source.zip')) + zipObj.write(os.path.join(ZipFileName), os.path.join(zipDirName, platform, 'esptool-source.zip')) sys.exit(0) diff --git a/fluidterm/fluidterm.py b/fluidterm/fluidterm.py index c732dc560..31ebf3c3d 100644 --- a/fluidterm/fluidterm.py +++ b/fluidterm/fluidterm.py @@ -20,9 +20,13 @@ if platform.system() == 'Darwin': import subprocess else: - from tkinter import * - from tkinter import filedialog - from tkinter import simpledialog + try: + from tkinter import * + from tkinter import filedialog + from tkinter import simpledialog + except: + pass + import time import serial @@ -498,6 +502,7 @@ def ask_for_port(): ports = [] if len(comports()) == 1: return comports()[0].device + sys.stderr.write('\n--- Available ports:\n') for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) @@ -686,8 +691,7 @@ def writer(self): # If you restart FluidNC with $bye or the reset switch, you # will have to trigger interactive mode manually time.sleep(2) # Time for FluidNC to be ready for input - right_arrow = '\x1b[C' - self.serial.write(self.tx_encoder.encode(right_arrow)) + self.enable_fluid_echo(); try: while self.alive: @@ -698,11 +702,10 @@ def writer(self): for c in data: if not self.alive: break - if menu_active: - self.handle_menu_key(c) - menu_active = False - elif c == self.menu_character: - menu_active = True # next char will be for menu + if c == '\x15': # CTRL+U -> upload file with XModem + self.upload_xmodem() + elif c == '\x12': # CTRL+R -> reset FluidNC + self.reset_fluidnc() elif c == self.exit_character: self.stop() # exit app break @@ -885,12 +888,32 @@ def file_dialog(self, initial): destname = self.mac_askstring(os.path.split(pathname)[1]) return (pathname, destname) else: - window = Tk() - pathname = filedialog.askopenfilename(title="File to Upload", initialfile=initial, filetypes=[("FluidNC Config", "*.yaml *.flnc *.txt"), ("All files", "*")]) - print("path",pathname) - destname = simpledialog.askstring("Uploader", "Destination Filename", initialvalue=os.path.split(pathname)[1]) - window.destroy() - return (pathname, destname) + try: + window = Tk() + except: + pathname = raw_input("Local file to send: ") + destname = raw_input("File on FluidNC: ") + return (pathname, destname) + else: + pathname = filedialog.askopenfilename(title="File to Upload", initialfile=initial, filetypes=[("FluidNC Config", "*.yaml *.flnc *.txt"), ("All files", "*")]) + print("path",pathname) + destname = simpledialog.askstring("Uploader", "Destination Filename", initialvalue=os.path.split(pathname)[1]) + window.destroy() + return (pathname, destname) + + def enable_fluid_echo(self): + right_arrow = '\x1b[C' + self.serial.write(self.tx_encoder.encode(right_arrow)) + + def reset_fluidnc(self): + """Pulse the reset line for FluidNC""" + self.console.write("Resetting MCU\n") + self.serial.rts = True + self.serial.dtr = False + time.sleep(1) + self.serial.rts = False + time.sleep(1) + self.enable_fluid_echo() def upload_xmodem(self): """Ask user for filename and send its contents""" @@ -1053,6 +1076,7 @@ def get_help_text(self): --- {exit:7} Send the exit character itself to remote --- {info:7} Show info --- {upload:7} Upload file (prompt will be shown) +--- {xmodem:7} Upload file via XMODEM (prompt will be shown) --- {repr:7} encoding --- {filter:7} edit filters --- Toggles: @@ -1076,6 +1100,7 @@ def get_help_text(self): echo=key_description('\x05'), info=key_description('\x09'), upload=key_description('\x15'), + xmodem=key_description('\x18'), repr=key_description('\x01'), filter=key_description('\x06'), eol=key_description('\x0c')) @@ -1295,11 +1320,10 @@ def main(default_port=None, default_baudrate=115200, default_rts=None, default_d if not args.quiet: sys.stderr.write('--- Fluidterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( p=miniterm.serial)) - sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( + sys.stderr.write('--- Quit: {} | Upload: {} | Reset: {} ---\n'.format( key_description(miniterm.exit_character), - key_description(miniterm.menu_character), - key_description(miniterm.menu_character), - key_description('H'))) + key_description('\x15'), + key_description('\x12'))) miniterm.start() try: diff --git a/install_scripts/linux-python3/checksecurity.sh b/install_scripts/linux-python3/checksecurity.sh deleted file mode 100644 index d2f0283f2..000000000 --- a/install_scripts/linux-python3/checksecurity.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -. ./tools.sh - -esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin - -if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then - echo ******************************************* - echo * Secure boot is enabled on this ESP32 * - echo * Loading FluidNC would probably brick it * - echo * !ABORTED! Read Wiki for more Info * - echo ******************************************* - cmp -l SecurityFuses.bin common/SecurityFusesOK.bin - rm SecurityFuses.bin - exit 1 -fi - -rm SecurityFuses.bin -exit 0 diff --git a/install_scripts/linux-python3/fluidterm.sh b/install_scripts/linux-python3/fluidterm.sh deleted file mode 100644 index 242112766..000000000 --- a/install_scripts/linux-python3/fluidterm.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# Install dependencies if needed -python3 -m pip install -q pyserial xmodem - -# Run fluidterm -python3 fluidterm/fluidterm.py $* - diff --git a/install_scripts/linux-python3/install-bt.sh b/install_scripts/linux-python3/install-bt.sh deleted file mode 100644 index c776cdb36..000000000 --- a/install_scripts/linux-python3/install-bt.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - -BuildType=bt - -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -esptool_write $Bootloader $Bootapp $Firmware $Partitions - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/linux-python3/install-fs.sh b/install_scripts/linux-python3/install-fs.sh deleted file mode 100644 index 74a1c0453..000000000 --- a/install_scripts/linux-python3/install-fs.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - -LocalFS="0x3d0000 wifi/spiffs.bin" - -esptool_write $LocalFS - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/linux-python3/install-wifi.sh b/install_scripts/linux-python3/install-wifi.sh deleted file mode 100644 index 1213d0e02..000000000 --- a/install_scripts/linux-python3/install-wifi.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - -BuildType=wifi - -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -esptool_write $Bootloader $Bootapp $Firmware $Partitions - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh deleted file mode 100644 index b6356fb5c..000000000 --- a/install_scripts/linux-python3/tools.sh +++ /dev/null @@ -1,42 +0,0 @@ -# Subroutines to call esptool with common arguments - -# Ensure local binary path is in $PATH (pip3 default target path) -echo "$PATH" | grep '$HOME\/\.local\/bin' 2>&1 >/dev/null -if test "$?" != "0"; then - export PATH="$HOME/.local/bin:$PATH" -fi - -# Install esptool.py if needed -which esptool.py 2>&1 >/dev/null -if test "$?" != "0"; then - echo esptool.py not found, attempting to install - python3 -m pip install -q linux-python3/esptool-source.zip - if test "$?" != "0"; then - echo esptool.py install failed - exit 1 - fi - which esptool.py 2>&1 >/dev/null - if test "$?" != "0"; then - echo esptool.py claims to have installed successfully, but cannot be found in PATH - echo PATH= $PATH - exit 1 - fi -fi - -EsptoolPath=esptool.py - -BaseArgs="--chip esp32 --baud 230400" - -SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" - -esptool_basic () { - echo echo $EsptoolPath $BaseArgs $* - $EsptoolPath $BaseArgs $BaseArgs $* - if test "$?" != "0"; then - echo esptool.py failed - exit 1 - fi -} -esptool_write () { - esptool_basic $SetupArgs $* -} diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux/HOWTO-INSTALL.txt similarity index 69% rename from install_scripts/linux-python3/HOWTO-INSTALL.txt rename to install_scripts/linux/HOWTO-INSTALL.txt index a381e3b24..1be49ad6e 100644 --- a/install_scripts/linux-python3/HOWTO-INSTALL.txt +++ b/install_scripts/linux/HOWTO-INSTALL.txt @@ -3,7 +3,7 @@ _Please note: These instructions apply to the case where you have downloaded a release bundle from https://github.com/bdring/FluidNC/releases - a zip file named -fluidnc-vN.N.N-linux-python3.zip . They do not apply to installing from +fluidnc-vN.N.N-linux.zip . They do not apply to installing from a source tree. This HOWTO file is present in the source tree so that it can be packed into a release bundle by automated release scripting, but it does not tell you how to install from source. For that, visit @@ -13,18 +13,12 @@ _ These install scripts use Python3 and the Python version of esptool. https://docs.espressif.com/projects/esptool/en/latest/esp32/ -They are suitable for any system supporting esptool.py -eg. non-intel (arm) linux systems, such as Raspberry PI's or -legacy machines running 32bit distributions. +Most Linux distributions already have Python3 preinstalled. If you +have problems, search the web for: "install python3 on " replacing +ls with the name of your Linux version (Raspbian, Debian, etc.). -Most Linux distributions will already have Python3 -preinstalled. If you have problems, search the web for: -"install python3 on " -replacing with the name and version of your operating system. - -The script will attempt to install esptool.py if it is not already -present, if this fails please see the esptool documentation and -forums for support. +The script will install esptool.py if necessary. If this fails please +see the esptool documentation and forums for support. https://docs.espressif.com/projects/esptool/en/latest/esp32/installation.html https://www.esp32.com/viewforum.php?f=23 _ @@ -39,14 +33,26 @@ you are reading and run one of the following commands: To install the WiFi version: sh install-wifi.sh To install the Bluetooth version: sh install-bt.sh To replace the ESP32 local filesystem: sh install-fs.sh +To erase everything on the ESP32: sh erase.sh +To just run FluidTerm: sh fluidterm.sh + +### Serial Port Permission Problems + +Linux systems often deny access to serial ports by default. This +often shows up as a "Permission denied" message. To fix that, +this command works on most Linux systems: + + sudo usermod -aG dialout $USER + +### Erasing the ESP32 -### Group membership Considerations +If your ESP32 already has other software on it, installing FluidNC +on top of that other software might not work right. You can get +rid of that other software with: -Your user should be a member of the `dialup` group in order to access -the USB serial device, you can use `id -a` to see your current -groups. To add yourself to the 'dialup' group you need to use: + sh erase.sh - sudo usermod -a -G dialup +Then you can install FluidNC with install-* commands mentioned above. ### Local Filesystem Considerations @@ -63,9 +69,12 @@ it via WebUI from wifi/index.html.gz herein. A good approach is to use install-fs only on your first FluidNC installation, to start from a clean slate. -### Running Fluidterm +### Running FluidTerm -The FluidNC install scripts run Fluidterm automatically at the end, +FluidTerm is a simple serial terminal emulator program with a few +features specific to FluidNC - it can upload files and reset the ESP32. + +The FluidNC install scripts run FluidTerm automatically at the end, but if you want to run it separately, you can type sh fluidterm.sh @@ -74,29 +83,18 @@ or just ./fluidterm.sh -### If Fluidterm Won't Start ... +### If FluidTerm Won't Start ... -Fluidterm is intended to be helpful but it is not absolutely necessary -for using FluidNC. Fluidterm lets you interact directly with FluidNC +FluidTerm is intended to be helpful but it is not absolutely necessary +for using FluidNC. FluidTerm lets you interact directly with FluidNC over a USB serial port. There are other ways to do that, so if you -have trouble running Fluidterm, you can just ignore it. - -On headless (no GUI installed) machines fluidterm may fail with: - - ModuleNotFoundError: No module named 'tkinter' - -In this case you will need to install the correct system package for tkinter; -- note that trying to install via pip/pypi is usually not sufficient. - - Debian/Raspbian: $ sudo apt install python3-tk - Fedora/RHEL: $ sudo dnf install python3-tkinter -etc.. +have trouble running FluidTerm, you can just ignore it. -### Alternatives to Fluidterm +### Alternatives to FluidTerm Most GCode sender programs have some way to send commands directly to FluidNC, but it can sometimes be helpful to bypass the complexity of -a sender and use a more direct path. Fluidterm is one such direct +a sender and use a more direct path. FluidTerm is one such direct path, but there are others, typically called "serial terminals". For Linux, there are many such programs, such as "screen", "minicom", "cu", @@ -145,5 +143,4 @@ on any unix-like platform where Python3 and esptool.py are available. However this is unsupported; and for advanced users only. For reference: FreeBSD(13): - User had to be added to 'dialer' group, and py-tkinter installed from - ports to enable fluidterm. + User had to be added to 'dialer' group diff --git a/install_scripts/linux/checksecurity.sh b/install_scripts/linux/checksecurity.sh new file mode 100644 index 000000000..79a593f97 --- /dev/null +++ b/install_scripts/linux/checksecurity.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +. ./tools.sh + +check_security diff --git a/install_scripts/linux/erase.sh b/install_scripts/linux/erase.sh new file mode 100644 index 000000000..d59829723 --- /dev/null +++ b/install_scripts/linux/erase.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if ! . ./tools.sh; then exit 1; fi + +esptool_erase + +deactivate diff --git a/install_scripts/linux/fluidterm.sh b/install_scripts/linux/fluidterm.sh new file mode 100644 index 000000000..5bcc2339f --- /dev/null +++ b/install_scripts/linux/fluidterm.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +if ! . ./tools.sh; then exit 1; fi + +run_fluidterm $* diff --git a/install_scripts/linux/install-bt.sh b/install_scripts/linux/install-bt.sh new file mode 100644 index 000000000..a7abd6d8b --- /dev/null +++ b/install_scripts/linux/install-bt.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +BuildType=bt + +if ! . ./tools.sh; then exit 1; fi + +install + +deactivate diff --git a/install_scripts/linux/install-fs.sh b/install_scripts/linux/install-fs.sh new file mode 100644 index 000000000..2e829f4c8 --- /dev/null +++ b/install_scripts/linux/install-fs.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +if ! . ./tools.sh; then exit 1; fi + +if ! check_security; then exit 1; fi + +LocalFS="0x3d0000 wifi/spiffs.bin" +esptool_write $LocalFS + +deactivate diff --git a/install_scripts/linux/install-wifi.sh b/install_scripts/linux/install-wifi.sh new file mode 100644 index 000000000..45470b5f1 --- /dev/null +++ b/install_scripts/linux/install-wifi.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +BuildType=wifi + +if ! . ./tools.sh; then exit 1; fi + +install + +deactivate diff --git a/install_scripts/linux/tools.sh b/install_scripts/linux/tools.sh new file mode 100644 index 000000000..530c6183e --- /dev/null +++ b/install_scripts/linux/tools.sh @@ -0,0 +1,100 @@ +#!/bin/sh +# Subroutines to call esptool with common arguments + +if test -d ~/.fluidnc_venv; then + . ~/.fluidnc_venv/bin/activate +else + if ! python3 -m venv ~/.fluidnc_venv; then + if which apt; then + sudo apt install python3-venv + if ! python3 -m venv ~/.fluidnc_venv; then + echo Unable to create a Python virtual environment + return 1 + fi + else + echo The Python venv module is not present on your system + echo and we were not able to install it automatically + return 1 + fi + fi + . ~/.fluidnc_venv/bin/activate + if ! python3 -m pip install xmodem pyserial; then + echo Installation of xmodem and pyserial modules failed + deactivate + return 1 + fi + if ! which esptool.py 2>&1 >/dev/null; then + echo esptool.py not found, attempting to install + + if ! python3 -m pip install -q linux/esptool-source.zip; then + echo Installation of esptool.py failed + deactivate + return 1 + fi + fi +fi + +EsptoolPath=esptool.py + +BaseArgs="--chip esp32 --baud 230400" + +SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" + +esptool_basic () { + echo $EsptoolPath $BaseArgs $* + $EsptoolPath $BaseArgs $BaseArgs $* + if test "$?" != "0"; then + echo esptool failed + exit 1 + fi +} +esptool_write () { + esptool_basic $SetupArgs $* +} +check_security() { + esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin + + if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then + if ! cmp -s SecurityFuses.bin common/SecurityFusesOK0.bin ; then + echo "*******************************************" + echo "* Secure boot is enabled on this ESP32 *" + echo "* Loading FluidNC would probably brick it *" + echo "* !ABORTED! Read Wiki for more Info *" + echo "*******************************************" + cmp -l SecurityFuses.bin common/SecurityFusesOK0.bin + rm SecurityFuses.bin + deactivate + return 1 + fi + fi + + rm SecurityFuses.bin + return 0 +} + +esptool_erase() { + if ! check_security; then + deactivate + return 1 + fi + esptool_basic erase_flash +} + +Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootapp="0xe000 common/boot_app0.bin" +Firmware="0x10000 ${BuildType}/firmware.bin" +Partitions="0x8000 ${BuildType}/partitions.bin" + +run_fluidterm() { + python3 fluidterm/fluidterm.py $* +} + +install() { + if ! check_security; then + exit + fi + esptool_write $Bootloader $Bootapp $Firmware $Partitions + + echo Starting fluidterm + run_fluidterm +} diff --git a/install_scripts/macos/checksecurity.sh b/install_scripts/macos/checksecurity.sh index 6307e46c0..79a593f97 100644 --- a/install_scripts/macos/checksecurity.sh +++ b/install_scripts/macos/checksecurity.sh @@ -2,23 +2,4 @@ . ./tools.sh -esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin - -if test "$?" != "0"; then - echo esptool failed - exit 1 -fi - -if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then - echo ******************************************* - echo * Secure boot is enabled on this ESP32 * - echo * Loading FluidNC would probably brick it * - echo * !ABORTED! Read Wiki for more Info * - echo ******************************************* - cmp -l SecurityFuses.bin common/SecurityFusesOK.bin - rm SecurityFuses.bin - exit 1 -fi - -rm SecurityFuses.bin -exit 0 +check_security diff --git a/install_scripts/macos/erase.sh b/install_scripts/macos/erase.sh new file mode 100644 index 000000000..002eb1c61 --- /dev/null +++ b/install_scripts/macos/erase.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +. ./tools.sh + +esptool_erase diff --git a/install_scripts/macos/install-bt.sh b/install_scripts/macos/install-bt.sh index c776cdb36..ee3694bbc 100644 --- a/install_scripts/macos/install-bt.sh +++ b/install_scripts/macos/install-bt.sh @@ -1,19 +1,6 @@ #!/bin/sh -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - BuildType=bt +. ./tools.sh -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -esptool_write $Bootloader $Bootapp $Firmware $Partitions - -echo Starting fluidterm -sh fluidterm.sh +install diff --git a/install_scripts/macos/install-fs.sh b/install_scripts/macos/install-fs.sh index 74a1c0453..e9f6dae7b 100644 --- a/install_scripts/macos/install-fs.sh +++ b/install_scripts/macos/install-fs.sh @@ -1,14 +1,10 @@ #!/bin/sh -if ! ./checksecurity.sh; then +. ./tools.sh + +if ! check_security; then exit fi -. ./tools.sh - LocalFS="0x3d0000 wifi/spiffs.bin" - esptool_write $LocalFS - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/macos/install-wifi.sh b/install_scripts/macos/install-wifi.sh index 1213d0e02..bb68aa487 100644 --- a/install_scripts/macos/install-wifi.sh +++ b/install_scripts/macos/install-wifi.sh @@ -1,19 +1,6 @@ #!/bin/sh -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - BuildType=wifi +. ./tools.sh -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -esptool_write $Bootloader $Bootapp $Firmware $Partitions - -echo Starting fluidterm -sh fluidterm.sh +install diff --git a/install_scripts/macos/tools.sh b/install_scripts/macos/tools.sh index f5f091425..92f5041c0 100644 --- a/install_scripts/macos/tools.sh +++ b/install_scripts/macos/tools.sh @@ -7,7 +7,7 @@ BaseArgs="--chip esp32 --baud 230400" SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" esptool_basic () { - echo echo $EsptoolPath $BaseArgs $* + echo $EsptoolPath $BaseArgs $* $EsptoolPath $BaseArgs $BaseArgs $* if test "$?" != "0"; then echo esptool failed @@ -17,3 +17,44 @@ esptool_basic () { esptool_write () { esptool_basic $SetupArgs $* } +check_security() { + esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin + + if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then + if ! cmp -s SecurityFuses.bin common/SecurityFusesOK0.bin ; then + echo "*******************************************" + echo "* Secure boot is enabled on this ESP32 *" + echo "* Loading FluidNC would probably brick it *" + echo "* !ABORTED! Read Wiki for more Info *" + echo "*******************************************" + cmp -l SecurityFuses.bin common/SecurityFusesOK0.bin + rm SecurityFuses.bin + return 1 + fi + fi + + rm SecurityFuses.bin + return 0 +} + +esptool_erase() { + if ! check_security; then + exit + fi + esptool_basic erase_flash +} + +Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootapp="0xe000 common/boot_app0.bin" +Firmware="0x10000 ${BuildType}/firmware.bin" +Partitions="0x8000 ${BuildType}/partitions.bin" + +install() { + if ! check_security; then + exit + fi + esptool_write $Bootloader $Bootapp $Firmware $Partitions + + echo Starting fluidterm + sh fluidterm.sh +} diff --git a/install_scripts/win64/checksecurity.bat b/install_scripts/win64/checksecurity.bat index 584e744b0..53c4e1f76 100644 --- a/install_scripts/win64/checksecurity.bat +++ b/install_scripts/win64/checksecurity.bat @@ -15,15 +15,18 @@ if not %ErrorLevel% equ 0 ( fc /b SecurityFuses.bin common\SecurityFusesOK.bin > nul 2>&1 if not %Errorlevel% equ 0 ( - echo ******************************************* - echo * Secure boot is enabled on this ESP32 * - echo * Loading FluidNC would probably brick it * - echo * !ABORTED! Read Wiki for more Info * - echo ******************************************* - fc /b SecurityFuses.bin common\SecurityFusesOK.bin - del SecurityFuses.bin - pause - exit /b 1 + fc /b SecurityFuses.bin common\SecurityFusesOK0.bin > nul 2>&1 + if not %Errorlevel% equ 0 ( + echo ******************************************* + echo * Secure boot is enabled on this ESP32 * + echo * Loading FluidNC would probably brick it * + echo * !ABORTED! Read Wiki for more Info * + echo ******************************************* + fc /b SecurityFuses.bin common\SecurityFusesOK0.bin + del SecurityFuses.bin + pause + exit /b 1 + ) ) del SecurityFuses.bin exit /b 0 diff --git a/install_scripts/win64/erase.bat b/install_scripts/win64/erase.bat new file mode 100644 index 000000000..530e26ce1 --- /dev/null +++ b/install_scripts/win64/erase.bat @@ -0,0 +1,16 @@ +@echo off + +call checksecurity.bat +if not %ErrorLevel% equ 0 ( + exit /b 1 +) + +set EsptoolPath=win64\esptool.exe + +set BaseArgs=--chip esp32 --baud 921600 +set SetupArgs=--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect + +echo %EsptoolPath% %BaseArgs% erase_flash +%EsptoolPath% %BaseArgs% erase_flash + +pause From 131ac6723f7e8c48c644bb65f47e28e2e656e699 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 14 Feb 2022 12:23:40 -1000 Subject: [PATCH 046/100] Non-windows is now posix --- build-release.py | 30 +++++++++--------- fluidterm/{README.md => README-FluidTerm.md} | 0 .../{linux => posix}/HOWTO-INSTALL.txt | 31 +++++++++++-------- .../{linux => posix}/checksecurity.sh | 0 install_scripts/{linux => posix}/erase.sh | 0 install_scripts/{linux => posix}/fluidterm.sh | 0 .../{linux => posix}/install-bt.sh | 0 .../{linux => posix}/install-fs.sh | 0 .../{linux => posix}/install-wifi.sh | 0 install_scripts/{linux => posix}/tools.sh | 2 +- 10 files changed, 33 insertions(+), 30 deletions(-) rename fluidterm/{README.md => README-FluidTerm.md} (100%) rename install_scripts/{linux => posix}/HOWTO-INSTALL.txt (86%) rename install_scripts/{linux => posix}/checksecurity.sh (100%) rename install_scripts/{linux => posix}/erase.sh (100%) rename install_scripts/{linux => posix}/fluidterm.sh (100%) rename install_scripts/{linux => posix}/install-bt.sh (100%) rename install_scripts/{linux => posix}/install-fs.sh (100%) rename install_scripts/{linux => posix}/install-wifi.sh (100%) rename install_scripts/{linux => posix}/tools.sh (98%) diff --git a/build-release.py b/build-release.py index 728cc6042..350a5ef05 100644 --- a/build-release.py +++ b/build-release.py @@ -82,27 +82,23 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): if buildEnv(envName, verbose=verbose) != 0: sys.exit(1) -for platform in ['win64', 'macos', 'linux']: +for platform in ['win64', 'posix']: print("Creating zip file for ", platform) terseOSName = { 'win64': 'win', - 'linux': 'linux', - 'macos': 'macos' + 'posix': 'posix', } scriptExtension = { 'win64': '.bat', - 'linux': '.sh', - 'macos': '.sh' + 'posix': '.sh', } exeExtension = { 'win64': '.exe', - 'linux': '', - 'macos': '' + 'posix': '', } withEsptoolBinary = { 'win64': True, - 'linux': False, - 'macos': True + 'posix': False, } zipDirName = os.path.join('fluidnc-' + tag + '-' + platform) @@ -138,17 +134,17 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): for obj in ['firmware.bin','partitions.bin']: zipObj.write(os.path.join(objPath, obj), os.path.join(zipDirName, envName, obj)) - # E.g. macos/install-wifi.sh -> install-wifi.sh + # E.g. posix/install-wifi.sh -> install-wifi.sh copyToZip(zipObj, platform, 'install-' + envName + scriptExtension[platform], zipDirName) for script in ['install-fs', 'fluidterm', 'checksecurity', 'erase', 'tools']: - # E.g. macos/fluidterm.sh -> fluidterm.sh + # E.g. posix/fluidterm.sh -> fluidterm.sh copyToZip(zipObj, platform, script + scriptExtension[platform], zipDirName) # Put the fluidterm code in the archive - for obj in ['fluidterm.py', 'README.md']: + for obj in ['fluidterm.py', 'README-FluidTerm.md']: fn = os.path.join('fluidterm', obj) - zipObj.write(fn, os.path.join(zipDirName, fn)) + zipObj.write(fn, os.path.join(zipDirName, os.path.join('common', obj))) if platform == 'win64': obj = 'fluidterm' + exeExtension[platform] @@ -161,13 +157,15 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): name = 'README-ESPTOOL.txt' EspRepo = 'https://github.com/espressif/esptool/releases/download/' + EsptoolVersion + '/' EspDir = 'esptool-' + EsptoolVersion + '-' + platform + zipObj.write(os.path.join(sharedPath, name), os.path.join(zipDirName, platform, + name.replace('.txt', '-' + EsptoolVersion + '.txt'))) else: name = 'README-ESPTOOL-SOURCE.txt' EspRepo = 'https://github.com/espressif/esptool/archive/refs/tags/' EspDir = EsptoolVersion + zipObj.write(os.path.join(sharedPath, name), os.path.join(zipDirName, 'common', + name.replace('.txt', '-' + EsptoolVersion + '.txt'))) - zipObj.write(os.path.join(sharedPath, name), os.path.join(zipDirName, platform, - name.replace('.txt', '-' + EsptoolVersion + '.txt'))) # Download and unzip from ESP repo ZipFileName = EspDir + '.zip' @@ -185,6 +183,6 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): info.external_attr = 0o100755 << 16 zipObj.writestr(info, zipReader.read(sourceFileName)) else: - zipObj.write(os.path.join(ZipFileName), os.path.join(zipDirName, platform, 'esptool-source.zip')) + zipObj.write(os.path.join(ZipFileName), os.path.join(zipDirName, 'common', 'esptool-source.zip')) sys.exit(0) diff --git a/fluidterm/README.md b/fluidterm/README-FluidTerm.md similarity index 100% rename from fluidterm/README.md rename to fluidterm/README-FluidTerm.md diff --git a/install_scripts/linux/HOWTO-INSTALL.txt b/install_scripts/posix/HOWTO-INSTALL.txt similarity index 86% rename from install_scripts/linux/HOWTO-INSTALL.txt rename to install_scripts/posix/HOWTO-INSTALL.txt index 1be49ad6e..a1d1a8f00 100644 --- a/install_scripts/linux/HOWTO-INSTALL.txt +++ b/install_scripts/posix/HOWTO-INSTALL.txt @@ -122,19 +122,9 @@ for installing it on other versions of Linux. ### What Can Go Wrong? -esptool.py is only available on Python3.6 and higher, if your system -does not support that you may be able to use the python2.7 version of -esptool.py by manually installing it first: - pip install esptool.py -The install_* scripts should then be able to complete the firmware install, -but will fail after that when they cannot run fluidterm. See above -for alternatives to fluidterm. - -If Python3.6+ is available on your system, but (for whatever reason) you -use a lower Python version as the system default you can use a -python virtual environment (venv) to do the install without needing to -modify your system-wide python environment: -https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ +Many of these programs need at least Python3.6. If you have an earlier +version of Python3, you will need to install a later version. Ask the +internet for help. ### FreeBSD and other *Nix *BSD platforms @@ -144,3 +134,18 @@ However this is unsupported; and for advanced users only. For reference: FreeBSD(13): User had to be added to 'dialer' group + +### Starting Over + +The first time that you run this installer, it creates a Python +"virtual environment" with all of the necessary packages. That can +take awhile, especially on slower systems like Raspberry Pi. After +that first time, the tools are already installed and subsequent runs +should be much faster, even if you download a new version of the +FluidNC installer. + +If you want to start fresh and reinstall all the packages from +scratch, you can just delete the directory that contains the +virtual environment with: + + rm -rf ~/.fluidnc_venv diff --git a/install_scripts/linux/checksecurity.sh b/install_scripts/posix/checksecurity.sh similarity index 100% rename from install_scripts/linux/checksecurity.sh rename to install_scripts/posix/checksecurity.sh diff --git a/install_scripts/linux/erase.sh b/install_scripts/posix/erase.sh similarity index 100% rename from install_scripts/linux/erase.sh rename to install_scripts/posix/erase.sh diff --git a/install_scripts/linux/fluidterm.sh b/install_scripts/posix/fluidterm.sh similarity index 100% rename from install_scripts/linux/fluidterm.sh rename to install_scripts/posix/fluidterm.sh diff --git a/install_scripts/linux/install-bt.sh b/install_scripts/posix/install-bt.sh similarity index 100% rename from install_scripts/linux/install-bt.sh rename to install_scripts/posix/install-bt.sh diff --git a/install_scripts/linux/install-fs.sh b/install_scripts/posix/install-fs.sh similarity index 100% rename from install_scripts/linux/install-fs.sh rename to install_scripts/posix/install-fs.sh diff --git a/install_scripts/linux/install-wifi.sh b/install_scripts/posix/install-wifi.sh similarity index 100% rename from install_scripts/linux/install-wifi.sh rename to install_scripts/posix/install-wifi.sh diff --git a/install_scripts/linux/tools.sh b/install_scripts/posix/tools.sh similarity index 98% rename from install_scripts/linux/tools.sh rename to install_scripts/posix/tools.sh index 530c6183e..01844fc54 100644 --- a/install_scripts/linux/tools.sh +++ b/install_scripts/posix/tools.sh @@ -86,7 +86,7 @@ Firmware="0x10000 ${BuildType}/firmware.bin" Partitions="0x8000 ${BuildType}/partitions.bin" run_fluidterm() { - python3 fluidterm/fluidterm.py $* + python3 common/fluidterm.py $* } install() { From f1b1bfa44c982a7045a020cec5d092b252d49783 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 14 Feb 2022 12:24:48 -1000 Subject: [PATCH 047/100] Checked in SecurityFusesOK0.bin --- install_scripts/SecurityFusesOK0.bin | Bin 0 -> 4 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 install_scripts/SecurityFusesOK0.bin diff --git a/install_scripts/SecurityFusesOK0.bin b/install_scripts/SecurityFusesOK0.bin new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 From 055733ceada40fff0e69fb22be7c7f22458772f4 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 14 Feb 2022 12:37:41 -1000 Subject: [PATCH 048/100] Renames and deleted dead directories --- build-release.py | 6 +- .../{ => common}/README-ESPTOOL-SOURCE.txt | 0 .../{ => common}/SecurityFusesOK.bin | Bin .../{ => common}/SecurityFusesOK0.bin | Bin install_scripts/linux-amd64/HOWTO-INSTALL.txt | 104 ------------------ install_scripts/linux-amd64/checksecurity.sh | 19 ---- install_scripts/linux-amd64/fluidterm.sh | 8 -- install_scripts/linux-amd64/install-bt.sh | 19 ---- install_scripts/linux-amd64/install-fs.sh | 14 --- install_scripts/linux-amd64/install-wifi.sh | 19 ---- install_scripts/linux-amd64/tools.sh | 19 ---- install_scripts/macos/HOWTO-INSTALL.txt | 97 ---------------- install_scripts/macos/checksecurity.sh | 5 - install_scripts/macos/erase.sh | 5 - install_scripts/macos/fluidterm.sh | 8 -- install_scripts/macos/install-bt.sh | 6 - install_scripts/macos/install-fs.sh | 10 -- install_scripts/macos/install-wifi.sh | 6 - install_scripts/macos/tools.sh | 60 ---------- .../{ => win64}/README-ESPTOOL.txt | 0 20 files changed, 3 insertions(+), 402 deletions(-) rename install_scripts/{ => common}/README-ESPTOOL-SOURCE.txt (100%) rename install_scripts/{ => common}/SecurityFusesOK.bin (100%) rename install_scripts/{ => common}/SecurityFusesOK0.bin (100%) delete mode 100644 install_scripts/linux-amd64/HOWTO-INSTALL.txt delete mode 100644 install_scripts/linux-amd64/checksecurity.sh delete mode 100644 install_scripts/linux-amd64/fluidterm.sh delete mode 100644 install_scripts/linux-amd64/install-bt.sh delete mode 100644 install_scripts/linux-amd64/install-fs.sh delete mode 100644 install_scripts/linux-amd64/install-wifi.sh delete mode 100644 install_scripts/linux-amd64/tools.sh delete mode 100644 install_scripts/macos/HOWTO-INSTALL.txt delete mode 100644 install_scripts/macos/checksecurity.sh delete mode 100644 install_scripts/macos/erase.sh delete mode 100644 install_scripts/macos/fluidterm.sh delete mode 100644 install_scripts/macos/install-bt.sh delete mode 100644 install_scripts/macos/install-fs.sh delete mode 100644 install_scripts/macos/install-wifi.sh delete mode 100644 install_scripts/macos/tools.sh rename install_scripts/{ => win64}/README-ESPTOOL.txt (100%) diff --git a/build-release.py b/build-release.py index 350a5ef05..7c2fe0e39 100644 --- a/build-release.py +++ b/build-release.py @@ -117,7 +117,7 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): bootapp = 'boot_app0.bin'; zipObj.write(os.path.join(tools, "partitions", bootapp), os.path.join(zipDirName, 'common', bootapp)) for secFuses in ['SecurityFusesOK.bin', 'SecurityFusesOK0.bin']: - zipObj.write(os.path.join(sharedPath, secFuses), os.path.join(zipDirName, 'common', secFuses)) + zipObj.write(os.path.join(sharedPath, 'common', secFuses), os.path.join(zipDirName, 'common', secFuses)) # Put FluidNC binaries, partition maps, and installers in the archive for envName in ['wifi','bt']: @@ -157,13 +157,13 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): name = 'README-ESPTOOL.txt' EspRepo = 'https://github.com/espressif/esptool/releases/download/' + EsptoolVersion + '/' EspDir = 'esptool-' + EsptoolVersion + '-' + platform - zipObj.write(os.path.join(sharedPath, name), os.path.join(zipDirName, platform, + zipObj.write(os.path.join(sharedPath, platform, name), os.path.join(zipDirName, platform, name.replace('.txt', '-' + EsptoolVersion + '.txt'))) else: name = 'README-ESPTOOL-SOURCE.txt' EspRepo = 'https://github.com/espressif/esptool/archive/refs/tags/' EspDir = EsptoolVersion - zipObj.write(os.path.join(sharedPath, name), os.path.join(zipDirName, 'common', + zipObj.write(os.path.join(sharedPath, 'common', name), os.path.join(zipDirName, 'common', name.replace('.txt', '-' + EsptoolVersion + '.txt'))) diff --git a/install_scripts/README-ESPTOOL-SOURCE.txt b/install_scripts/common/README-ESPTOOL-SOURCE.txt similarity index 100% rename from install_scripts/README-ESPTOOL-SOURCE.txt rename to install_scripts/common/README-ESPTOOL-SOURCE.txt diff --git a/install_scripts/SecurityFusesOK.bin b/install_scripts/common/SecurityFusesOK.bin similarity index 100% rename from install_scripts/SecurityFusesOK.bin rename to install_scripts/common/SecurityFusesOK.bin diff --git a/install_scripts/SecurityFusesOK0.bin b/install_scripts/common/SecurityFusesOK0.bin similarity index 100% rename from install_scripts/SecurityFusesOK0.bin rename to install_scripts/common/SecurityFusesOK0.bin diff --git a/install_scripts/linux-amd64/HOWTO-INSTALL.txt b/install_scripts/linux-amd64/HOWTO-INSTALL.txt deleted file mode 100644 index 0811482b7..000000000 --- a/install_scripts/linux-amd64/HOWTO-INSTALL.txt +++ /dev/null @@ -1,104 +0,0 @@ -## Installing FluidNC on your ESP32 - -_Please note: These instructions apply to the case where you have -downloaded a release bundle from -https://github.com/bdring/FluidNC/releases - a zip file named -fluidnc-vN.N.N-linux-amd64.zip . They do not apply to installing from -a source tree. This HOWTO file is present in the source tree so that -it can be packed into a release bundle by automated release scripting, -but it does not tell you how to install from source. For that, visit -https://github.com/bdring/FluidNC/wiki/FluidNC-Compiling#use-vs-code--platformio-to-compile -_ - -Unpack this FluidNC release Zip file. - -Plug your ESP32 into a USB port. - -At a shell prompt, cd to the directory that contains the file -you are reading and run one of the following commands: - -To install the WiFi version: sh install-wifi.sh -To install the Bluetooth version: sh install-bt.sh -To replace the ESP32 local filesystem: sh install-fs.sh - -### Local Filesystem Considerations - -Replacing the local filesystem is only useful for the wifi version. -The Bluetooth version can start with an empty local filesystem. - -The disadvantage of replacing the local filesystem is that it will -overwrite any files that you might already have there, such as FluidNC -config files, WebUI preferences and macros. The advantage is that you -will get the latest version of the index.html.gz file that contains -the WebUI code. Another way to get a new index.html.gz is to upload -it via WebUI from wifi/index.html.gz herein. - -A good approach is to use install-fs only on your first FluidNC -installation, to start from a clean slate. - -### Running Fluidterm - -The FluidNC install scripts run Fluidterm automatically at the end, -but if you want to run it separately, you can type - - sh fluidterm.sh - -or just - - ./fluidterm.sh - -### If Fluidterm Won't Start ... - -Fluidterm is intended to be helpful but it is not absolutely necessary -for using FluidNC. Fluidterm lets you interact directly with FluidNC -over a USB serial port. There are other ways to do that, so if you -have trouble running Fluidterm, you can just ignore it. - -If Fluidterm does not work immediately, you can probably get it to work -by installing Python3 - the language in which Fluidterm is written. -Most Linux distributions will already have Python3 preinstalled. -If you have problems, search the web for "install python3 on linux". - -### Alternatives to Fluidterm - -Most GCode sender programs have some way to send commands directly to -FluidNC, but it can sometimes be helpful to bypass the complexity of -a sender and use a more direct path. Fluidterm is one such direct -path, but there are others, typically called "serial terminals". - -For Linux, there are many such programs, such as "screen", "minicom", "cu", -"picocom", and "PuTTY". The one that is most likely to be preinstalled -is named "screen". If screen is not preinstalled, you might be able to -install it with (on Ubuntu or Debian). - - sudo apt update - sudo apt install screen - -To use screen, go to a shell window and type: - - ls /dev/tty* - -That will give you a list of serial ports. You need to find the one -that is connected to FluidNC. It will probably have a name that starts -with "/dev/ttyUSB". Once you have found that name, type - - screen /dev/ttyUSBWHATEVER 115200 - -To exit from screen, type Control-A k - -Search the web for more documentation about screen, or for instructions -for installing it on other versions of Linux. - -### What Can Go Wrong? - -If you see an error like this: - - [9481] Error loading Python lib '/tmp/_MEIoyh8ER/libpython3.8.so.1.0': dlopen: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by /tmp/_MEIoyh8ER/libpython3.8.so.1.0) - -.. it could be version mismatches between system libraries and the -precompiled esptool executable in the linux-amd64 directory. - -The workaround is to run the Python version of esptool - esptool.py - under Python, -instead of using the esptool executable version. The Internet has many tutorials -for using esptool.py . You can find the appropriate command line arguments in the -.sh files. diff --git a/install_scripts/linux-amd64/checksecurity.sh b/install_scripts/linux-amd64/checksecurity.sh deleted file mode 100644 index d2f0283f2..000000000 --- a/install_scripts/linux-amd64/checksecurity.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -. ./tools.sh - -esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin - -if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then - echo ******************************************* - echo * Secure boot is enabled on this ESP32 * - echo * Loading FluidNC would probably brick it * - echo * !ABORTED! Read Wiki for more Info * - echo ******************************************* - cmp -l SecurityFuses.bin common/SecurityFusesOK.bin - rm SecurityFuses.bin - exit 1 -fi - -rm SecurityFuses.bin -exit 0 diff --git a/install_scripts/linux-amd64/fluidterm.sh b/install_scripts/linux-amd64/fluidterm.sh deleted file mode 100644 index 242112766..000000000 --- a/install_scripts/linux-amd64/fluidterm.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# Install dependencies if needed -python3 -m pip install -q pyserial xmodem - -# Run fluidterm -python3 fluidterm/fluidterm.py $* - diff --git a/install_scripts/linux-amd64/install-bt.sh b/install_scripts/linux-amd64/install-bt.sh deleted file mode 100644 index c776cdb36..000000000 --- a/install_scripts/linux-amd64/install-bt.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - -BuildType=bt - -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -esptool_write $Bootloader $Bootapp $Firmware $Partitions - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/linux-amd64/install-fs.sh b/install_scripts/linux-amd64/install-fs.sh deleted file mode 100644 index 74a1c0453..000000000 --- a/install_scripts/linux-amd64/install-fs.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - -LocalFS="0x3d0000 wifi/spiffs.bin" - -esptool_write $LocalFS - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/linux-amd64/install-wifi.sh b/install_scripts/linux-amd64/install-wifi.sh deleted file mode 100644 index 1213d0e02..000000000 --- a/install_scripts/linux-amd64/install-wifi.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if ! ./checksecurity.sh; then - exit -fi - -. ./tools.sh - -BuildType=wifi - -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -esptool_write $Bootloader $Bootapp $Firmware $Partitions - -echo Starting fluidterm -sh fluidterm.sh diff --git a/install_scripts/linux-amd64/tools.sh b/install_scripts/linux-amd64/tools.sh deleted file mode 100644 index 51eddbe16..000000000 --- a/install_scripts/linux-amd64/tools.sh +++ /dev/null @@ -1,19 +0,0 @@ -# Subroutines to call esptool with common arguments - -EsptoolPath=linux-amd64/esptool - -BaseArgs="--chip esp32 --baud 230400" - -SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" - -esptool_basic () { - echo echo $EsptoolPath $BaseArgs $* - $EsptoolPath $BaseArgs $BaseArgs $* - if test "$?" != "0"; then - echo esptool failed - exit 1 - fi -} -esptool_write () { - esptool_basic $SetupArgs $* -} diff --git a/install_scripts/macos/HOWTO-INSTALL.txt b/install_scripts/macos/HOWTO-INSTALL.txt deleted file mode 100644 index 7890d7dfe..000000000 --- a/install_scripts/macos/HOWTO-INSTALL.txt +++ /dev/null @@ -1,97 +0,0 @@ -## Installing FluidNC on your ESP32 - -_Please note: These instructions apply to the case where you have -downloaded a release bundle from -https://github.com/bdring/FluidNC/releases - a zip file named -fluidnc-vN.N.N-macos.zip . They do not apply to installing from a -source tree. This HOWTO file is present in the source tree so that it -can be packed into a release bundle by automated release scripting, -but it does not tell you how to install from source. For that, visit -https://github.com/bdring/FluidNC/wiki/FluidNC-Compiling#use-vs-code--platformio-to-compile -_ - -* Unpack this FluidNC release Zip file. - -* Plug your ESP32 into a USB port. - -* In a Terminal window, cd to the directory that contains the -file you are reading and run one of the following: - -To install the WiFi version: sh install-wifi.sh -To install the Bluetooth version: sh install-bt.sh -To replace the ESP32 local filesystem: sh install-fs.sh - -### Local Filesystem Considerations - -Replacing the local filesystem is only useful for the WiFi version. -The Bluetooth version can start with an empty local filesystem. - -The disadvantage of replacing the local filesystem is that it will -overwrite any files that you might already have there, such as FluidNC -config files, WebUI preferences and macros. The advantage is that you -will get the latest version of the index.html.gz file that contains -the WebUI code. Another way to get a new index.html.gz is to upload -it via WebUI from wifi/index.html.gz herein. - -A good approach is to use install-fs only on your first FluidNC -installation, or to start from a clean slate. - -### Running Fluidterm - -The FluidNC install scripts run Fluidterm automatically at the end, -but if you want to run it separately, you can type - - sh fluidterm.sh - -or just - - ./fluidterm.sh - -### If Fluidterm Won't Start ... - -Fluidterm is intended to be helpful but it is not absolutely necessary -for using FluidNC. Fluidterm lets you interact directly with FluidNC -over a USB serial port. There are other ways to do that, so if you -have trouble running Fluidterm, you can just ignore it. - -If Fluidterm does not work immediately, you can probably get it to work -by installing Python3 - the language in which Fluidterm is written. -MacOS has Python preinstalled, but, up until very recent MacOS versions, -the preinstalled Python was Python2, which has been obsolete since Jan 2020. - -Instructions for installing Python 3 on MacOS can be found at -https://www.freecodecamp.org/news/python-version-on-mac-update/ - -Or just search the web for "macos install python3". - -### Alternatives to Fluidterm - -Most GCode sender programs have some way to send commands directly to -FluidNC, but it can sometimes be helpful to bypass the complexity of -a sender and use a more direct path. Fluidterm is one such direct -path, but there are others. On MacOS, there is a preinstalled program -named "screen" that will do the job. To use screen, go to a Terminal -window and type: - - ls /dev/tty* - -That will give you a list of serial ports. You need to find the one -that is connected to FluidNC. It will probably have a name that starts -with "/dev/tty.usbserial". Once you have found that name, type - - screen /dev/tty.usbserialWHATEVER 115200 - -To exit from screen, type Control-A k - -Search the web for more documentation about screen. - -### What Can Go Wrong? - -Strange failures can be caused by version mismatches between system -libraries and the precompiled esptool executable in the macos -directory. - -The workaround is to run the Python version of esptool - esptool.py - -under Python, instead of using the esptool executable version. The -Internet has many tutorials for using esptool.py . You can find the -appropriate command line arguments in the .sh files. diff --git a/install_scripts/macos/checksecurity.sh b/install_scripts/macos/checksecurity.sh deleted file mode 100644 index 79a593f97..000000000 --- a/install_scripts/macos/checksecurity.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -. ./tools.sh - -check_security diff --git a/install_scripts/macos/erase.sh b/install_scripts/macos/erase.sh deleted file mode 100644 index 002eb1c61..000000000 --- a/install_scripts/macos/erase.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -. ./tools.sh - -esptool_erase diff --git a/install_scripts/macos/fluidterm.sh b/install_scripts/macos/fluidterm.sh deleted file mode 100644 index 242112766..000000000 --- a/install_scripts/macos/fluidterm.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# Install dependencies if needed -python3 -m pip install -q pyserial xmodem - -# Run fluidterm -python3 fluidterm/fluidterm.py $* - diff --git a/install_scripts/macos/install-bt.sh b/install_scripts/macos/install-bt.sh deleted file mode 100644 index ee3694bbc..000000000 --- a/install_scripts/macos/install-bt.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -BuildType=bt -. ./tools.sh - -install diff --git a/install_scripts/macos/install-fs.sh b/install_scripts/macos/install-fs.sh deleted file mode 100644 index e9f6dae7b..000000000 --- a/install_scripts/macos/install-fs.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -. ./tools.sh - -if ! check_security; then - exit -fi - -LocalFS="0x3d0000 wifi/spiffs.bin" -esptool_write $LocalFS diff --git a/install_scripts/macos/install-wifi.sh b/install_scripts/macos/install-wifi.sh deleted file mode 100644 index bb68aa487..000000000 --- a/install_scripts/macos/install-wifi.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -BuildType=wifi -. ./tools.sh - -install diff --git a/install_scripts/macos/tools.sh b/install_scripts/macos/tools.sh deleted file mode 100644 index 92f5041c0..000000000 --- a/install_scripts/macos/tools.sh +++ /dev/null @@ -1,60 +0,0 @@ -# Subroutines to call esptool with common arguments - -EsptoolPath=macos/esptool - -BaseArgs="--chip esp32 --baud 230400" - -SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" - -esptool_basic () { - echo $EsptoolPath $BaseArgs $* - $EsptoolPath $BaseArgs $BaseArgs $* - if test "$?" != "0"; then - echo esptool failed - exit 1 - fi -} -esptool_write () { - esptool_basic $SetupArgs $* -} -check_security() { - esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin - - if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then - if ! cmp -s SecurityFuses.bin common/SecurityFusesOK0.bin ; then - echo "*******************************************" - echo "* Secure boot is enabled on this ESP32 *" - echo "* Loading FluidNC would probably brick it *" - echo "* !ABORTED! Read Wiki for more Info *" - echo "*******************************************" - cmp -l SecurityFuses.bin common/SecurityFusesOK0.bin - rm SecurityFuses.bin - return 1 - fi - fi - - rm SecurityFuses.bin - return 0 -} - -esptool_erase() { - if ! check_security; then - exit - fi - esptool_basic erase_flash -} - -Bootloader="0x1000 common/bootloader_dio_80m.bin" -Bootapp="0xe000 common/boot_app0.bin" -Firmware="0x10000 ${BuildType}/firmware.bin" -Partitions="0x8000 ${BuildType}/partitions.bin" - -install() { - if ! check_security; then - exit - fi - esptool_write $Bootloader $Bootapp $Firmware $Partitions - - echo Starting fluidterm - sh fluidterm.sh -} diff --git a/install_scripts/README-ESPTOOL.txt b/install_scripts/win64/README-ESPTOOL.txt similarity index 100% rename from install_scripts/README-ESPTOOL.txt rename to install_scripts/win64/README-ESPTOOL.txt From b4df6b2858dcad7829a0e8d1ba4bcd92bd0b2d79 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sat, 12 Feb 2022 10:21:44 -1000 Subject: [PATCH 049/100] Limits redux --- FluidNC/src/Jog.cpp | 8 ++-- FluidNC/src/Kinematics/Cartesian.cpp | 22 ----------- FluidNC/src/Kinematics/Cartesian.h | 1 - FluidNC/src/Kinematics/CoreXY.cpp | 5 +-- FluidNC/src/Kinematics/CoreXY.h | 1 - FluidNC/src/Kinematics/Kinematics.cpp | 11 +----- FluidNC/src/Kinematics/Kinematics.h | 2 - FluidNC/src/Kinematics/WallPlotter.cpp | 19 +++------ FluidNC/src/Kinematics/WallPlotter.h | 17 ++++----- FluidNC/src/Limits.cpp | 53 +++++++++++++++++++++----- FluidNC/src/Limits.h | 5 ++- FluidNC/src/Machine/Axes.h | 10 ----- FluidNC/src/MotionControl.cpp | 14 +------ 13 files changed, 69 insertions(+), 99 deletions(-) diff --git a/FluidNC/src/Jog.cpp b/FluidNC/src/Jog.cpp index d6d4d35da..7bb374c6e 100644 --- a/FluidNC/src/Jog.cpp +++ b/FluidNC/src/Jog.cpp @@ -7,6 +7,7 @@ #include "Machine/MachineConfig.h" #include "MotionControl.h" // mc_linear #include "Stepper.h" // st_prep_buffer, st_wake_up +#include "Limits.h" // constrainToSoftLimits() // Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog. // cancelledInflight will be set to true if was not added to parser due to a cancelJog. @@ -18,11 +19,8 @@ Error jog_execute(plan_line_data_t* pl_data, parser_block_t* gc_block, bool* can pl_data->is_jog = true; pl_data->line_number = gc_block->values.n; - if (config->_axes->hasSoftLimits()) { - if (config->_kinematics->limitsCheckTravel(gc_block->values.xyz)) { - return Error::TravelExceeded; - } - } + constrainToSoftLimits(gc_block->values.xyz); + // Valid jog command. Plan, set state, and execute. if (!mc_linear(gc_block->values.xyz, pl_data, gc_state.position)) { return Error::JogCancelled; diff --git a/FluidNC/src/Kinematics/Cartesian.cpp b/FluidNC/src/Kinematics/Cartesian.cpp index 8cb7c6d6f..612748859 100644 --- a/FluidNC/src/Kinematics/Cartesian.cpp +++ b/FluidNC/src/Kinematics/Cartesian.cpp @@ -1,6 +1,5 @@ #include "Cartesian.h" -#include "../Limits.h" #include "../Machine/MachineConfig.h" namespace Kinematics { @@ -25,27 +24,6 @@ namespace Kinematics { memcpy(cartesian, motors, n_axis * sizeof(motors[0])); } - // Checks and reports if target array exceeds machine travel limits. - // Return true if exceeding limits - bool Cartesian::limitsCheckTravel(float* target) { - auto axes = config->_axes; - auto n_axis = config->_axes->_numberAxis; - - float cartesian[MAX_N_AXIS]; - motors_to_cartesian(cartesian, target, n_axis); // Convert to cartesian then check - - bool limit_error = false; - for (int axis = 0; axis < n_axis; axis++) { - auto axisSetting = axes->_axis[axis]; - if (axisSetting->_softLimits && (cartesian[axis] < limitsMinPosition(axis) || cartesian[axis] > limitsMaxPosition(axis))) { - String axis_letter = String(Machine::Axes::_names[axis]); - log_info("Soft limit on " << axis_letter << " target:" << cartesian[axis]); - limit_error = true; - } - } - return limit_error; - } - // Configuration registration namespace { KinematicsFactory::InstanceBuilder registration("Cartesian"); diff --git a/FluidNC/src/Kinematics/Cartesian.h b/FluidNC/src/Kinematics/Cartesian.h index 20e6ea06b..1de8c0d58 100644 --- a/FluidNC/src/Kinematics/Cartesian.h +++ b/FluidNC/src/Kinematics/Cartesian.h @@ -27,7 +27,6 @@ namespace Kinematics { virtual void init() override; virtual bool kinematics_homing(AxisMask cycle_mask) override; virtual void kinematics_post_homing() override; - virtual bool limitsCheckTravel(float* target) override; virtual void motors_to_cartesian(float* cartesian, float* motors, int n_axis) override; // Configuration handlers: diff --git a/FluidNC/src/Kinematics/CoreXY.cpp b/FluidNC/src/Kinematics/CoreXY.cpp index 3e89ad07d..16ff23205 100644 --- a/FluidNC/src/Kinematics/CoreXY.cpp +++ b/FluidNC/src/Kinematics/CoreXY.cpp @@ -1,7 +1,7 @@ #include "CoreXY.h" #include "../Machine/MachineConfig.h" -#include "../Limits.h" // limits_soft_check +#include "../Limits.h" // ambiguousLimit() #include "../Machine/Homing.h" #include "../Protocol.h" // protocol_execute_realtime @@ -149,9 +149,6 @@ namespace Kinematics { } if (ambiguousLimit()) { - // TODO: Maybe ambiguousLimit() should do this stuff because this could be a several places - mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown - rtAlarm = ExecAlarm::HardLimit; log_error("Ambiguous limit switch touching. Manually clear all switches"); return true; } diff --git a/FluidNC/src/Kinematics/CoreXY.h b/FluidNC/src/Kinematics/CoreXY.h index 43cd8545f..1e065758f 100644 --- a/FluidNC/src/Kinematics/CoreXY.h +++ b/FluidNC/src/Kinematics/CoreXY.h @@ -32,7 +32,6 @@ namespace Kinematics { void kinematics_post_homing() override; bool cartesian_to_motors(float* target, plan_line_data_t* pl_data, float* position) override; void motors_to_cartesian(float* cartesian, float* motors, int n_axis) override; - //bool limitsCheckTravel(float* target) override; // Configuration handlers: void validate() const override {} diff --git a/FluidNC/src/Kinematics/Kinematics.cpp b/FluidNC/src/Kinematics/Kinematics.cpp index ebc2a07d3..ad1c0ed26 100644 --- a/FluidNC/src/Kinematics/Kinematics.cpp +++ b/FluidNC/src/Kinematics/Kinematics.cpp @@ -28,11 +28,6 @@ namespace Kinematics { return _system->motors_to_cartesian(cartesian, motors, n_axis); } - bool Kinematics::limitsCheckTravel(float* target) { - Assert(_system != nullptr, "No kinematic system"); - return _system->limitsCheckTravel(target); - } - void Kinematics::group(Configuration::HandlerBase& handler) { ::Kinematics::KinematicsFactory::factory(handler, _system); Assert(_system != nullptr, "No kinematics system."); @@ -46,10 +41,8 @@ namespace Kinematics { void Kinematics::init() { Assert(_system != nullptr, "init: Kinematics system missing."); - _system->init(); + _system->init(); } - Kinematics::~Kinematics() { - delete _system; - } + Kinematics::~Kinematics() { delete _system; } }; diff --git a/FluidNC/src/Kinematics/Kinematics.h b/FluidNC/src/Kinematics/Kinematics.h index 27450ab23..baf09cf1f 100644 --- a/FluidNC/src/Kinematics/Kinematics.h +++ b/FluidNC/src/Kinematics/Kinematics.h @@ -46,7 +46,6 @@ namespace Kinematics { void kinematics_post_homing(); bool cartesian_to_motors(float* target, plan_line_data_t* pl_data, float* position); void motors_to_cartesian(float* cartesian, float* motors, int n_axis); - bool limitsCheckTravel(float* target); private: ::Kinematics::KinematicSystem* _system = nullptr; @@ -66,7 +65,6 @@ namespace Kinematics { virtual void init() = 0; virtual bool kinematics_homing(AxisMask cycle_mask) = 0; virtual void kinematics_post_homing() = 0; - virtual bool limitsCheckTravel(float* target) = 0; virtual void motors_to_cartesian(float* cartesian, float* motors, int n_axis) = 0; // Configuration interface. diff --git a/FluidNC/src/Kinematics/WallPlotter.cpp b/FluidNC/src/Kinematics/WallPlotter.cpp index 73ce3760b..19d0185d2 100644 --- a/FluidNC/src/Kinematics/WallPlotter.cpp +++ b/FluidNC/src/Kinematics/WallPlotter.cpp @@ -86,10 +86,10 @@ namespace Kinematics { if (segment_count == 0 && target[Z_AXIS] != position[Z_AXIS]) { // We are moving vertically. last_z = target[Z_AXIS]; - + // Note that the left motor runs backward. float cables[MAX_N_AXIS] = { 0 - (last_left - zero_left), 0 + (last_right - zero_right), last_z }; - + if (!mc_move_motors(cables, pl_data)) { return false; } @@ -112,7 +112,7 @@ namespace Kinematics { float cx, cy; // These are absolute. lengths_to_xy(seg_left, seg_right, cx, cy); - + if (abs(seg_x - cx) > 0.1 || abs(seg_y - cy) > 0.1) { // FIX: Produce an alarm state? } @@ -147,21 +147,13 @@ namespace Kinematics { // Note that the left motor runs backward. float absolute_x, absolute_y; lengths_to_xy((0 - motors[_left_axis]) + zero_left, (0 + motors[_right_axis]) + zero_right, absolute_x, absolute_y); - + // Producing these relative coordinates. cartesian[X_AXIS] = absolute_x; cartesian[Y_AXIS] = absolute_y; cartesian[Z_AXIS] = motors[Z_AXIS]; - - // Now we have a number that if fed back into the system should produce the same value. - } - /* - limitsCheckTravel() is called to check soft limits - It returns true if the motion is outside the limit values - */ - bool WallPlotter::limitsCheckTravel(float* target) { - return false; + // Now we have a number that if fed back into the system should produce the same value. } /* @@ -219,7 +211,6 @@ namespace Kinematics { right_length = sqrt(right_dx * right_dx + right_dy * right_dy); } - // Configuration registration namespace { KinematicsFactory::InstanceBuilder registration("WallPlotter"); diff --git a/FluidNC/src/Kinematics/WallPlotter.h b/FluidNC/src/Kinematics/WallPlotter.h index ed2057cbe..c984f9211 100644 --- a/FluidNC/src/Kinematics/WallPlotter.h +++ b/FluidNC/src/Kinematics/WallPlotter.h @@ -28,7 +28,6 @@ namespace Kinematics { void kinematics_post_homing() override; bool cartesian_to_motors(float* target, plan_line_data_t* pl_data, float* position) override; void motors_to_cartesian(float* cartesian, float* motors, int n_axis) override; - bool limitsCheckTravel(float* target) override; // Configuration handlers: void validate() const override {} @@ -45,20 +44,20 @@ namespace Kinematics { void xy_to_lengths(float x, float y, float& left_length, float& right_length); // State - float zero_left; // The left cord offset corresponding to cartesian (0, 0). - float zero_right; // The right cord offset corresponding to cartesian (0, 0). - float last_left; // The last produced left cord length. - float last_right; // The last produced right cord length. - float last_z; // The last produced z value. + float zero_left; // The left cord offset corresponding to cartesian (0, 0). + float zero_right; // The right cord offset corresponding to cartesian (0, 0). + float last_left; // The last produced left cord length. + float last_right; // The last produced right cord length. + float last_z; // The last produced z value. // Parameters - int _left_axis = 0; + int _left_axis = 0; float _left_anchor_x = -100; float _left_anchor_y = 100; - int _right_axis = 1; + int _right_axis = 1; float _right_anchor_x = 100; float _right_anchor_y = 100; float _segment_length = 10; }; -} // namespace Kinematics +} // namespace Kinematics diff --git a/FluidNC/src/Limits.cpp b/FluidNC/src/Limits.cpp index 3f1dfba02..e86c767d8 100644 --- a/FluidNC/src/Limits.cpp +++ b/FluidNC/src/Limits.cpp @@ -5,10 +5,10 @@ #include "Limits.h" #include "Machine/MachineConfig.h" -#include "MotionControl.h" // mc_reset -#include "System.h" // sys.* -#include "Protocol.h" // protocol_execute_realtime -#include "Platform.h" // WEAK_LINK +#include "MotionControl.h" // mc_reset +#include "System.h" // sys.* +#include "Protocol.h" // protocol_execute_realtime +#include "Platform.h" // WEAK_LINK #include #include @@ -44,16 +44,53 @@ MotorMask limits_get_state() { } bool ambiguousLimit() { - return Machine::Axes::posLimitMask & Machine::Axes::negLimitMask; + if (Machine::Axes::posLimitMask & Machine::Axes::negLimitMask) { + mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. + rtAlarm = ExecAlarm::HardLimit; + return true; + } + return false; } bool soft_limit = false; +// Constrain the coordinates to stay within the soft limit envelope +void constrainToSoftLimits(float* cartesian) { + auto axes = config->_axes; + auto n_axis = config->_axes->_numberAxis; + + bool limit_error = false; + for (int axis = 0; axis < n_axis; axis++) { + auto axisSetting = axes->_axis[axis]; + if (axisSetting->_softLimits) { + if (cartesian[axis] < limitsMinPosition(axis)) { + cartesian[axis] = limitsMinPosition(axis); + } + if (cartesian[axis] > limitsMaxPosition(axis)) { + cartesian[axis] = limitsMaxPosition(axis); + } + } + } +} + // Performs a soft limit check. Called from mcline() only. Assumes the machine has been homed, // the workspace volume is in all negative space, and the system is in normal operation. // NOTE: Used by jogging to limit travel within soft-limit volume. -void limits_soft_check(float* target) { - if (config->_kinematics->limitsCheckTravel(target)) { +void limits_soft_check(float* cartesian) { + bool limit_error = false; + + auto axes = config->_axes; + auto n_axis = config->_axes->_numberAxis; + + for (int axis = 0; axis < n_axis; axis++) { + if (axes->_axis[axis]->_softLimits && (cartesian[axis] < limitsMinPosition(axis) || cartesian[axis] > limitsMaxPosition(axis))) { + String axis_letter = String(Machine::Axes::_names[axis]); + log_info("Soft limit on " << axis_letter << " target:" << cartesian[axis]); + limit_error = true; + } + } + + if (limit_error) { soft_limit = true; // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within // workspace volume so just come to a controlled stop so position is not lost. When complete @@ -72,7 +109,6 @@ void limits_soft_check(float* target) { mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. rtAlarm = ExecAlarm::SoftLimit; // Indicate soft limit critical event protocol_execute_realtime(); // Execute to enter critical event loop and system abort - return; } } @@ -117,4 +153,3 @@ float limitsMinPosition(size_t axis) { //return (homing == nullptr || homing->_positiveDirection) ? mpos : mpos - maxtravel; return (homing == nullptr || homing->_positiveDirection) ? mpos - maxtravel : mpos; } - diff --git a/FluidNC/src/Limits.h b/FluidNC/src/Limits.h index c446b6085..e24e85bf2 100644 --- a/FluidNC/src/Limits.h +++ b/FluidNC/src/Limits.h @@ -17,7 +17,10 @@ void limits_init(); MotorMask limits_get_state(); // Check for soft limit violations -void limits_soft_check(float* target); +void limits_soft_check(float* cartesian); + +// Constrain the coordinates to stay within the soft limit envelope +void constrainToSoftLimits(float* cartesian); float limitsMaxPosition(size_t axis); float limitsMinPosition(size_t axis); diff --git a/FluidNC/src/Machine/Axes.h b/FluidNC/src/Machine/Axes.h index b141b2227..4dde41836 100644 --- a/FluidNC/src/Machine/Axes.h +++ b/FluidNC/src/Machine/Axes.h @@ -13,7 +13,6 @@ namespace MotorDrivers { namespace Machine { class Axes : public Configuration::Configurable { - bool _switchedStepper = false; // During homing, this is used to stop stepping on motors that have @@ -44,15 +43,6 @@ namespace Machine { size_t findAxisIndex(const MotorDrivers::MotorDriver* const motor) const; size_t findAxisMotor(const MotorDrivers::MotorDriver* const motor) const; - inline bool hasSoftLimits() const { - for (int i = 0; i < _numberAxis; ++i) { - if (_axis[i]->_softLimits) { - return true; - } - } - return false; - } - inline bool hasHardLimits() const { for (int axis = 0; axis < _numberAxis; ++axis) { auto a = _axis[axis]; diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index 50847af87..b3522dc76 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -50,14 +50,6 @@ bool mc_move_motors(float* target, plan_line_data_t* pl_data) { // store the plan data so it can be cancelled by the protocol system if needed mc_pl_data_inflight = pl_data; - // If enabled, check for soft limit violations. - bool hasSoftLimits = config->_axes->hasSoftLimits(); - if (hasSoftLimits) { - // NOTE: Block jog state. Jogging is a special case and soft limits are handled independently. - if (sys.state != State::Jog) { - limits_soft_check(target); - } - } // If in check gcode mode, prevent motion by blocking planner. Soft limits still work. if (sys.state == State::CheckMode) { mc_pl_data_inflight = NULL; @@ -137,7 +129,7 @@ void mc_arc(float* target, float rt_axis0 = target[axis_0] - center_axis0; float rt_axis1 = target[axis_1] - center_axis1; - auto n_axis = config->_axes->_numberAxis; + auto n_axis = config->_axes->_numberAxis; float previous_position[n_axis] = { 0.0 }; for (size_t i = 0; i < n_axis; i++) { @@ -170,7 +162,7 @@ void mc_arc(float* target, pl_data->feed_rate *= segments; pl_data->motion.inverseTime = 0; // Force as feed absolute mode over arc segments. } - float theta_per_segment = angular_travel / segments; + float theta_per_segment = angular_travel / segments; float linear_per_segment[n_axis]; linear_per_segment[axis_linear] = (target[axis_linear] - position[axis_linear]) / segments; for (size_t i = A_AXIS; i < n_axis; i++) { @@ -272,8 +264,6 @@ void mc_homing_cycle(AxisMask axis_mask) { // or if it is impossible to tell which end is engaged. In that situation // we do not know the pulloff direction. if (ambiguousLimit()) { - mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. - rtAlarm = ExecAlarm::HardLimit; return; } From da885c932389d925265bc662c04e52d9a1641c85 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 11 Feb 2022 13:37:20 -1000 Subject: [PATCH 050/100] Do not scale rotary axis report values in report_inches mode --- FluidNC/src/Report.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/FluidNC/src/Report.cpp b/FluidNC/src/Report.cpp index 233f20b3a..655a2d840 100644 --- a/FluidNC/src/Report.cpp +++ b/FluidNC/src/Report.cpp @@ -81,15 +81,25 @@ static const int axesStringLen = coordStringLen * MAX_N_AXIS; // Sends the axis values to the output channel static void report_util_axis_values(const float* axis_value, Print& channel) { - float unit_conv = 1.0; // unit conversion multiplier..default is mm - int decimals = 3; // Default - report mm to 3 decimal places - if (config->_reportInches) { - unit_conv = 1.0f / MM_PER_INCH; - decimals = 4; // Report inches to 4 decimal places - } auto n_axis = config->_axes->_numberAxis; for (size_t idx = 0; idx < n_axis; idx++) { - channel << setprecision(decimals) << (axis_value[idx] * unit_conv); + int decimals; + float value = axis_value[idx]; + if (idx >= A_AXIS && idx <= C_AXIS) { + // Rotary axes are in degrees so mm vs inch is not + // relevant. Three decimal places is probably overkill + // for rotary axes but we use 3 in case somebody wants + // to use ABC as linear axes in mm. + decimals = 3; + } else { + if (config->_reportInches) { + value /= MM_PER_INCH; + decimals = 4; // Report inches to 4 decimal places + } else { + decimals = 3; // Report mm to 3 decimal places + } + } + channel << setprecision(decimals) << value; if (idx < (n_axis - 1)) { channel << ","; } From c2199fcace187cc290a706f46c94d0a848a90624 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 11 Feb 2022 14:44:09 -1000 Subject: [PATCH 051/100] Fix input conversion of rotary axes too --- FluidNC/src/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FluidNC/src/GCode.cpp b/FluidNC/src/GCode.cpp index 275826825..26aa0da34 100644 --- a/FluidNC/src/GCode.cpp +++ b/FluidNC/src/GCode.cpp @@ -878,7 +878,7 @@ Error gc_execute_line(char* line, Channel& channel) { // Pre-convert XYZ coordinate values to millimeters, if applicable. if (gc_block.modal.units == Units::Inches) { for (size_t idx = 0; idx < n_axis; idx++) { // Axes indices are consistent, so loop may be used. - if (bitnum_is_true(axis_words, idx)) { + if ((idx < A_AXIS || idx > C_AXIS) && bitnum_is_true(axis_words, idx)) { gc_block.values.xyz[idx] *= MM_PER_INCH; } } From 4f9de7f98a3b79ab900d3ab0cbf8ef61023be11b Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Tue, 15 Feb 2022 14:01:47 -1000 Subject: [PATCH 052/100] Fixed limits, jogging, and mpos setting --- FluidNC/src/Machine/Homing.cpp | 13 +++---------- FluidNC/src/MotionControl.cpp | 1 + 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/FluidNC/src/Machine/Homing.cpp b/FluidNC/src/Machine/Homing.cpp index 2b66df29a..2dd8490ec 100644 --- a/FluidNC/src/Machine/Homing.cpp +++ b/FluidNC/src/Machine/Homing.cpp @@ -263,19 +263,12 @@ namespace Machine { // Set machine positions for homed limit switches. Don't update non-homed axes. for (int axis = 0; axis < n_axis; axis++) { - Machine::Axis* axisConf = config->_axes->_axis[axis]; - auto homing = axisConf->_homing; - if (bitnum_is_true(axisMask, axis)) { - auto mpos = homing->_mpos; - auto pulloff = axisConf->_motors[0]->_pulloff; - - mpos += homing->_positiveDirection ? -pulloff : pulloff; - motor_steps[axis] = mpos_to_steps(mpos, axis); + motor_steps[axis] = mpos_to_steps(axes->_axis[axis]->_homing->_mpos, axis); } } - sys.step_control = {}; // Return step control to normal operation. - config->_axes->set_homing_mode(axisMask, false); // tell motors homing is done + sys.step_control = {}; // Return step control to normal operation. + axes->set_homing_mode(axisMask, false); // tell motors homing is done } void Homing::run_one_cycle(AxisMask axisMask) { diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index b3522dc76..ee11f267b 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -103,6 +103,7 @@ void mc_cancel_jog() { // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. bool mc_linear(float* target, plan_line_data_t* pl_data, float* position) { + limits_soft_check(target); return config->_kinematics->cartesian_to_motors(target, pl_data, position); } From 719a41f54ba84c2954a9fc1d2cba607159adcf15 Mon Sep 17 00:00:00 2001 From: bdring Date: Wed, 16 Feb 2022 12:03:22 -0600 Subject: [PATCH 053/100] Update CoreXY.cpp --- FluidNC/src/Kinematics/CoreXY.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/FluidNC/src/Kinematics/CoreXY.cpp b/FluidNC/src/Kinematics/CoreXY.cpp index 16ff23205..73f139593 100644 --- a/FluidNC/src/Kinematics/CoreXY.cpp +++ b/FluidNC/src/Kinematics/CoreXY.cpp @@ -221,10 +221,7 @@ namespace Kinematics { if (axisConf->_homing) { auto mpos_mm = axisConf->_homing->_mpos; - auto pulloff = axisConf->_motors[0]->_pulloff; - - pulloff = axisConf->_homing->_positiveDirection ? -pulloff : pulloff; - mpos[axis] = mpos_mm + pulloff; + mpos[axis] = mpos_mm; } } From 618403d70e223a3019e38282e8d7f680f5396bb7 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Wed, 16 Feb 2022 11:32:48 -1000 Subject: [PATCH 054/100] Fixed $limits/show cancel time problem --- FluidNC/src/ProcessSettings.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/FluidNC/src/ProcessSettings.cpp b/FluidNC/src/ProcessSettings.cpp index 0151e8227..6f0d7c049 100644 --- a/FluidNC/src/ProcessSettings.cpp +++ b/FluidNC/src/ProcessSettings.cpp @@ -358,33 +358,39 @@ static Error home_c(const char* value, WebUI::AuthenticationLevel auth_level, Ch static void write_limit_set(uint32_t mask, Channel& out) { const char* motor0AxisName = "xyzabc"; for (int i = 0; i < MAX_N_AXIS; i++) { - out.write(bitnum_is_true(mask, i) ? char(motor0AxisName[i]) : ' '); + out << (bitnum_is_true(mask, i) ? char(motor0AxisName[i]) : ' '); } const char* motor1AxisName = "XYZABC"; for (int i = 0; i < MAX_N_AXIS; i++) { - out.write(bitnum_is_true(mask, i + 16) ? char(motor1AxisName[i]) : ' '); + out << (bitnum_is_true(mask, i + 16) ? char(motor1AxisName[i]) : ' '); } } static Error show_limits(const char* value, WebUI::AuthenticationLevel auth_level, Channel& out) { out.print("Send ! to exit\n"); out.print("Homing Axes: "); write_limit_set(Machine::Axes::homingMask, out); - out.write('\n'); - out.print("Limit Axes: "); + out << '\n'; + out << "Limit Axes: "; write_limit_set(Machine::Axes::limitMask, out); - out.write('\n'); - out.print(" PosLimitPins NegLimitPins\n"); + out << '\n'; + out << " PosLimitPins NegLimitPins\n"; + const TickType_t interval = 500; + TickType_t limit = xTaskGetTickCount(); do { - out.print(": "); // Prevents WebUI from suppressing an empty line - write_limit_set(Machine::Axes::posLimitMask, out); - out.write(' '); - write_limit_set(Machine::Axes::negLimitMask, out); - out.print("\r\n"); - vTaskDelay(500); // Delay for a reasonable repeat rate + TickType_t thisTime = xTaskGetTickCount(); + if (((long)(thisTime - limit)) > 0) { + out << ": "; // Prevents WebUI from suppressing an empty line + write_limit_set(Machine::Axes::posLimitMask, out); + out << ' '; + write_limit_set(Machine::Axes::negLimitMask, out); + out << '\n'; + limit = thisTime + interval; + } + vTaskDelay(1); pollChannels(); } while (!rtFeedHold); rtFeedHold = false; - out.write('\n'); + out << '\n'; return Error::Ok; } static Error go_to_sleep(const char* value, WebUI::AuthenticationLevel auth_level, Channel& out) { From 6fad1f4ee4ba8d990df7108c3808b9eb8f5b8334 Mon Sep 17 00:00:00 2001 From: bdring Date: Fri, 18 Feb 2022 09:57:31 -0600 Subject: [PATCH 055/100] Update I2SOPinDetail.cpp --- FluidNC/src/Pins/I2SOPinDetail.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FluidNC/src/Pins/I2SOPinDetail.cpp b/FluidNC/src/Pins/I2SOPinDetail.cpp index c4a1b5a6e..f571f4985 100644 --- a/FluidNC/src/Pins/I2SOPinDetail.cpp +++ b/FluidNC/src/Pins/I2SOPinDetail.cpp @@ -60,7 +60,7 @@ namespace Pins { Assert(value.validateWith(this->_capabilities), "Requested attributes do not match the I2SO pin capabilities"); Assert(!_attributes.conflictsWith(value), "Attributes on this pin have been set before, and there's a conflict."); - _attributes = value; + _attributes = _attributes | value; // I2S out pins cannot be configured, hence there // is nothing to do here for them. We basically From 1591f756a5915a7aebb5e940574f9315edfbd8b8 Mon Sep 17 00:00:00 2001 From: Xek Date: Thu, 24 Feb 2022 20:46:02 +0300 Subject: [PATCH 056/100] Fix wifi+bt startup crash (#311) * Fixed timing-dependent hang with M5 (#282) * fix wifi+bt startup crash Co-authored-by: bdring Co-authored-by: Mitch Bradley Co-authored-by: Sergey Averin --- FluidNC/src/Main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 89e08af1e..9417bb7b6 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -122,8 +122,9 @@ void setup() { sys.state = State::ConfigAlarm; } - WebUI::wifi_config.begin(); - WebUI::bt_config.begin(); + if (!WebUI::wifi_config.begin()) { + WebUI::bt_config.begin(); + } WebUI::inputBuffer.begin(); allChannels.deregistration(&startupLog); } From 607c200dc604a1101ef81d65915d964a33ab8990 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Thu, 24 Feb 2022 08:38:28 -1000 Subject: [PATCH 057/100] Added erase flash and other info to Windows install instructions (#306) --- install_scripts/win64/HOWTO-INSTALL.txt | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/install_scripts/win64/HOWTO-INSTALL.txt b/install_scripts/win64/HOWTO-INSTALL.txt index 3e9e223c0..b556bef9d 100644 --- a/install_scripts/win64/HOWTO-INSTALL.txt +++ b/install_scripts/win64/HOWTO-INSTALL.txt @@ -20,6 +20,20 @@ name as below). To install the WiFi version: install-wifi.bat To install the Bluetooth version: install-bt.bat To replace the ESP32 local filesystem: install-fs.bat +To erase everything on the ESP32: erase.bat +To just run FluidTerm: fluidterm.bat + +### Erasing the ESP32 + +If your ESP32 already has other software on it, installing FluidNC +on top of that other software might not work right. You can get +rid of that other software with: + + erase.bat + +Then you can install FluidNC with install-* commands mentioned above. + +### Local Filesystem Considerations Replacing the local filesystem is only useful for the wifi version, since its default contents are only needed for @@ -35,3 +49,28 @@ it via WebUI from wifi/index.html.gz herein. A good approach is to use install-fs only on your first FluidNC installation, or to start from a clean slate. + +### Running FluidTerm + +FluidTerm is a simple serial terminal emulator program with a few +features specific to FluidNC - it can upload files and reset the ESP32. + +The FluidNC install scripts run FluidTerm automatically at the end, +but if you want to run it separately, you can type + + fluidterm.bat + +### Alternatives to FluidTerm + +Most GCode sender programs have some way to send commands directly to +FluidNC, but it can sometimes be helpful to bypass the complexity of +a sender and use a more direct path. FluidTerm is one such direct +path, but there are others, typically called "serial terminals". + +For Windows, there are many such programs, such as "PuTTY" and +"TeraTerm Pro". None are preinstalled by default, but you can +download one from the internet. Setting one up requires that +you know which COM port is for the ESP32, the baud rate (115200), +and some other settings - no hardware or software flow control, +accept linefeed as end-of-line on input, etc. FluidTerm tried +to make it easy for you by using all the correct defaults. From f62016ffdb2675efd206ece909e5463fa896dda3 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Thu, 24 Feb 2022 08:40:15 -1000 Subject: [PATCH 058/100] Fix sd reinsert (#313) * Added erase flash and other info to Windows install instructions * Fixed sd/list after card removal Test: 1. Insert SD card and $sd/list 2. Remove SD card and $sd/list 3. Insert SD card and $sd/list Before the fix, step 3 would continue to say No SD Card --- FluidNC/src/SDCard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FluidNC/src/SDCard.cpp b/FluidNC/src/SDCard.cpp index a4516910b..60aa25ca7 100644 --- a/FluidNC/src/SDCard.cpp +++ b/FluidNC/src/SDCard.cpp @@ -64,7 +64,7 @@ SDCard::State SDCard::test_or_open(bool refresh) { } SDCard::State SDCard::begin(SDCard::State newState) { - if (newState != SDCard::State::Idle && _state > SDCard::State::Idle) { + if (_state >= SDCard::State::Busy) { return _state; } SDCard::State oldState = test_or_open(true); From 80eb94373a4844577d6bda2eca9b07db4acf8bf8 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Fri, 25 Feb 2022 08:39:58 -1000 Subject: [PATCH 059/100] Fluid term fix (#307) * Added erase flash and other info to Windows install instructions * Ensure that fluidterm.exe exits when its parent window dies Previously, if you killed a console window in which fluidterm.exe was running, the fluidterm process would continue to run in the background, preventing other processes from using the COM port. --- fluidterm/README-FluidTerm.md | 5 +++++ fluidterm/fluidterm.exe | Bin 2360586 -> 2361105 bytes 2 files changed, 5 insertions(+) diff --git a/fluidterm/README-FluidTerm.md b/fluidterm/README-FluidTerm.md index 0229bdb67..3f65cd986 100644 --- a/fluidterm/README-FluidTerm.md +++ b/fluidterm/README-FluidTerm.md @@ -30,3 +30,8 @@ Changes You can restart the ESP32 to see the boot messages with the FluidNC **$bye** command or you can toggle the DTR function to restart most ESP32 modules by doing Ctrl+T Ctrl+D twice. + +### Source Code + +The source code for fluidterm.py (the Python version) is in this directory. The source code for +fluidterm.exe (the Windows native version) is at https://github.com/MitchBradley/FluidTerm2 . diff --git a/fluidterm/fluidterm.exe b/fluidterm/fluidterm.exe index 181b9461d7e26b1950382fbe93153d3246ee8551..8ed54dbf53add42bc5b76e600fbd7d640ce01e69 100644 GIT binary patch delta 277778 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�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�fa!T93;l@K2|U2h8v>pabWQ+oGI-`!vEKx8 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

-EI6ayxlqt1#j7I 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 Date: Fri, 18 Feb 2022 11:36:42 -1000 Subject: [PATCH 060/100] Fixed Feedhold after Soft Limit If you trigger a soft limit, then issue a feedhold while running a program, the system goes into Hold state but does not stop moving. --- FluidNC/src/Protocol.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/FluidNC/src/Protocol.cpp b/FluidNC/src/Protocol.cpp index ff436c438..086ee5ea5 100644 --- a/FluidNC/src/Protocol.cpp +++ b/FluidNC/src/Protocol.cpp @@ -101,6 +101,7 @@ bool can_park() { void protocol_reset() { probeState = ProbeState::Off; + soft_limit = false; rtStatusReport = false; rtCycleStart = false; rtFeedHold = false; From 2391b04b3b9371e3860a9a978bcc40d7fab78d90 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sat, 26 Feb 2022 08:41:16 -1000 Subject: [PATCH 061/100] WebUI dashboard and tablet file lists conflict - #321 (#323) --- FluidNC/data/index.html.gz | Bin 122734 -> 122395 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/FluidNC/data/index.html.gz b/FluidNC/data/index.html.gz index 274369aa7719b7e4ec04bcab50fb0d0de7c7d317..0a1b68081f3ac61baa07be0ba71695e9ae1bdb06 100644 GIT binary patch delta 105414 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 delta 105803 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

*dM!^Au+bWL;CFCwYAENH1M2!s+CM`9D5n$48V` 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 E02_6Z8~^|S From eab616565d5ee1d4de11359b096da8149c130739 Mon Sep 17 00:00:00 2001 From: bdring Date: Sat, 26 Feb 2022 12:11:27 -0600 Subject: [PATCH 062/100] Update CoreXY.cpp Cut/paste/edit error. It should be checking both X and Y axes --- FluidNC/src/Kinematics/CoreXY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FluidNC/src/Kinematics/CoreXY.cpp b/FluidNC/src/Kinematics/CoreXY.cpp index 73f139593..5429ce483 100644 --- a/FluidNC/src/Kinematics/CoreXY.cpp +++ b/FluidNC/src/Kinematics/CoreXY.cpp @@ -154,7 +154,7 @@ namespace Kinematics { } if (cycle_mask != 0) { - if (bitnum_is_true(cycle_mask, X_AXIS) || bitnum_is_true(cycle_mask, X_AXIS)) { + if (bitnum_is_true(cycle_mask, X_AXIS) || bitnum_is_true(cycle_mask, Y_AXIS)) { log_error("CoreXY cannot single axis home X or Y axes"); // TODO: Set some Kinematics error or alarm return true; From 875ab66555690e0de91e5bd02eb1d7e1e29ad393 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sat, 26 Feb 2022 14:18:00 -1000 Subject: [PATCH 063/100] Constrain config values - #309 (#326) * Constrain config values Laser pwm_hz was not range checked and constrained. That caused crashes when it was 0. Added a constrain with message to handler.item. unit32_t and float Updated the classes that were separately constraining to use this method. * Use hardware limiting values for PWM frequency. Co-authored-by: bdring --- FluidNC/src/Configuration/ParserHandler.h | 4 ++- FluidNC/src/Motors/RcServo.cpp | 3 -- FluidNC/src/Motors/RcServo.h | 6 ++-- FluidNC/src/Motors/Solenoid.cpp | 6 ---- FluidNC/src/Motors/Solenoid.h | 10 +++---- FluidNC/src/Spindles/Laser.h | 3 +- FluidNC/src/Spindles/PWMSpindle.cpp | 34 +++++++++++------------ FluidNC/src/Spindles/PWMSpindle.h | 14 +++++++++- 8 files changed, 41 insertions(+), 39 deletions(-) diff --git a/FluidNC/src/Configuration/ParserHandler.h b/FluidNC/src/Configuration/ParserHandler.h index 787e00b51..a49bfd0e0 100644 --- a/FluidNC/src/Configuration/ParserHandler.h +++ b/FluidNC/src/Configuration/ParserHandler.h @@ -52,7 +52,7 @@ namespace Configuration { #endif if (_parser.token_.indent_ > thisIndent) { log_error("Skipping key " << _parser.key().str() << " indent " << _parser.token_.indent_ << " thisIndent " - << thisIndent); + << thisIndent); } else { #ifdef DEBUG_VERBOSE_YAML_PARSER log_debug("Parsing key " << _parser.key().str()); @@ -102,6 +102,7 @@ namespace Configuration { void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override { if (_parser.is(name)) { value = _parser.intValue(); + constrain_with_message(value, minValue, maxValue); } } @@ -120,6 +121,7 @@ namespace Configuration { void item(const char* name, float& value, float minValue, float maxValue) override { if (_parser.is(name)) { value = _parser.floatValue(); + constrain_with_message(value, minValue, maxValue); } } diff --git a/FluidNC/src/Motors/RcServo.cpp b/FluidNC/src/Motors/RcServo.cpp index 8246387ce..9be3fe26b 100644 --- a/FluidNC/src/Motors/RcServo.cpp +++ b/FluidNC/src/Motors/RcServo.cpp @@ -32,9 +32,6 @@ namespace MotorDrivers { // RcServo::RcServo(Pin pwm_pin) : Servo(), _pwm_pin(pwm_pin) {} void RcServo::init() { - constrain_with_message(_pwm_freq, SERVO_PWM_FREQ_MIN, SERVO_PWM_FREQ_MAX); - constrain_with_message(_min_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX); - constrain_with_message(_max_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX); if (_output_pin.undefined()) { log_warn(" RC Servo disabled: No output pin"); _has_errors = true; diff --git a/FluidNC/src/Motors/RcServo.h b/FluidNC/src/Motors/RcServo.h index ea03972dd..0a1e001e7 100644 --- a/FluidNC/src/Motors/RcServo.h +++ b/FluidNC/src/Motors/RcServo.h @@ -45,9 +45,9 @@ namespace MotorDrivers { // Configuration handlers: void group(Configuration::HandlerBase& handler) override { handler.item("output_pin", _output_pin); - handler.item("pwm_hz", _pwm_freq); - handler.item("min_pulse_us", _min_pulse_us); - handler.item("max_pulse_us", _max_pulse_us); + handler.item("pwm_hz", _pwm_freq, SERVO_PWM_FREQ_MIN, SERVO_PWM_FREQ_MAX); + handler.item("min_pulse_us", _min_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX); + handler.item("max_pulse_us", _max_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX); } // Name of the configurable. Must match the name registered in the cpp file. diff --git a/FluidNC/src/Motors/Solenoid.cpp b/FluidNC/src/Motors/Solenoid.cpp index ba3cf3235..95a23b7fe 100644 --- a/FluidNC/src/Motors/Solenoid.cpp +++ b/FluidNC/src/Motors/Solenoid.cpp @@ -48,12 +48,6 @@ namespace MotorDrivers { void Solenoid::init() { - constrain_with_message(_pwm_freq, (uint32_t)1000, (uint32_t)10000); - constrain_with_message(_off_percent, 0.0f, 100.0f); - constrain_with_message(_pull_percent, 0.0f, 100.0f); - constrain_with_message(_hold_percent, 0.0f, 100.0f); - constrain_with_message(_pull_ms, (uint32_t)0, (uint32_t)3000); - if (_output_pin.undefined()) { log_warn(" Solenoid disabled: No output pin"); _has_errors = true; diff --git a/FluidNC/src/Motors/Solenoid.h b/FluidNC/src/Motors/Solenoid.h index 0bc773e6b..8e46e2e25 100644 --- a/FluidNC/src/Motors/Solenoid.h +++ b/FluidNC/src/Motors/Solenoid.h @@ -39,11 +39,11 @@ namespace MotorDrivers { // Configuration handlers: void group(Configuration::HandlerBase& handler) override { handler.item("output_pin", _output_pin); - handler.item("pwm_hz", _pwm_freq); - handler.item("off_percent", _off_percent); - handler.item("pull_percent", _pull_percent); - handler.item("hold_percent", _hold_percent); - handler.item("pull_ms", _pull_ms); + handler.item("pwm_hz", _pwm_freq, 1000, 100000); + handler.item("off_percent", _off_percent, 0.0f, 100.0f); + handler.item("pull_percent", _pull_percent, 0.0f, 100.0f); + handler.item("hold_percent", _hold_percent, 0.0f, 100.0f); + handler.item("pull_ms", _pull_ms, 0, 3000); handler.item("direction_invert", _dir_invert); } diff --git a/FluidNC/src/Spindles/Laser.h b/FluidNC/src/Spindles/Laser.h index 360e32e4d..b2acb8474 100644 --- a/FluidNC/src/Spindles/Laser.h +++ b/FluidNC/src/Spindles/Laser.h @@ -35,8 +35,7 @@ namespace Spindles { // pwm_freq is the only item that the PWM class adds to OnOff // We cannot call PWM::group() because that would pick up // direction_pin, which we do not want in Laser - handler.item("pwm_hz", _pwm_freq); - + handler.item("pwm_hz", _pwm_freq, 1000, 100000); OnOff::groupCommon(handler); } diff --git a/FluidNC/src/Spindles/PWMSpindle.cpp b/FluidNC/src/Spindles/PWMSpindle.cpp index f62986d90..5df01735a 100644 --- a/FluidNC/src/Spindles/PWMSpindle.cpp +++ b/FluidNC/src/Spindles/PWMSpindle.cpp @@ -20,11 +20,6 @@ namespace Spindles { void PWM::init() { - if (_pwm_freq == 0) { - log_error(name() << " PWM frequency is 0."); - return; - } - get_pins_and_settings(); setupSpeeds(_pwm_freq); @@ -129,24 +124,27 @@ namespace Spindles { ledcSetDuty(_pwm_chan_num, duty); } - /* - Calculate the highest precision of a PWM based on the frequency in bits - - 80,000,000 / freq = period - determine the highest precision where (1 << precision) < period - */ + // Calculate the highest PWM precision in bits for the desired frequency + // 80,000,000 (APB Clock) = freq * maxCount + // maxCount is a power of two between 2^1 and 2^20 + // frequency is at most 80,000,000 / 2^1 = 40,000,000, limited elsewhere + // to 20,000,000 to give a period of at least 2^2 = 4 levels of control. uint8_t PWM::calc_pwm_precision(uint32_t freq) { - uint8_t precision = 0; if (freq == 0) { - return precision; + freq = 1; // Limited elsewhere but just to be safe... } - // increase the precision (bits) until it exceeds allow by frequency the max or is 16 - while ((1u << precision) < uint32_t(80000000 / freq) && precision <= 16) { - precision++; + // Increase the precision (bits) until it exceeds the frequency + // The hardware maximum precision is 20 bits + const uint8_t ledcMaxBits = 20; + const uint32_t apbFreq = 80000000; + const uint32_t maxCount = apbFreq / freq; + for (uint8_t bits = 2; bits <= ledcMaxBits; ++bits) { + if ((1u << bits) > maxCount) { + return bits - 1; + } } - - return precision - 1; + return ledcMaxBits; } void PWM::deinit() { diff --git a/FluidNC/src/Spindles/PWMSpindle.h b/FluidNC/src/Spindles/PWMSpindle.h index a6464aab6..127b0a3cd 100644 --- a/FluidNC/src/Spindles/PWMSpindle.h +++ b/FluidNC/src/Spindles/PWMSpindle.h @@ -35,7 +35,19 @@ namespace Spindles { void validate() const override { Spindle::validate(); } void group(Configuration::HandlerBase& handler) override { - handler.item("pwm_hz", _pwm_freq); + // The APB clock frequency is 80MHz and the maximum divisor + // is 2^10. The maximum precision is 2^20. 80MHz/2^(20+10) + // is 0.075 Hz, or one cycle in 13.4 seconds. We cannot + // represent that in an integer so we set the minimum + // frequency to 1 Hz. Frequencies of 76 Hz or less use + // the full 20 bit resolution, 77 to 152 Hz uses 19 bits, + // 153 to 305 uses 18 bits, ... + // At the other end, the minimum useful precision is 2^2 + // or 4 levels of control, so the max is 80MHz/2^2 = 20MHz. + // Those might not be practical for many CNC applications, + // but the ESP32 hardware can handle them, so we let the + // user choose. + handler.item("pwm_hz", _pwm_freq, 1, 20000000); OnOff::group(handler); } From add81a89f88a15459258e586aec16021da65a332 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sat, 26 Feb 2022 14:07:05 -1000 Subject: [PATCH 064/100] Added axes/shared_stepper_reset_pin #309 --- FluidNC/src/Machine/Axes.cpp | 7 +++++++ FluidNC/src/Machine/Axes.h | 1 + 2 files changed, 8 insertions(+) diff --git a/FluidNC/src/Machine/Axes.cpp b/FluidNC/src/Machine/Axes.cpp index 03a626d83..f405bceb9 100644 --- a/FluidNC/src/Machine/Axes.cpp +++ b/FluidNC/src/Machine/Axes.cpp @@ -29,6 +29,12 @@ namespace Machine { _sharedStepperDisable.report("Shared stepper disable"); } + if (_sharedStepperReset.defined()) { + _sharedStepperReset.setAttr(Pin::Attr::Output | Pin::Attr::InitialOn); + _sharedStepperReset.on(); + _sharedStepperReset.report("Shared stepper reset"); + } + unlock_all_motors(); // certain motors need features to be turned on. Check them here @@ -189,6 +195,7 @@ namespace Machine { void Axes::group(Configuration::HandlerBase& handler) { handler.item("shared_stepper_disable_pin", _sharedStepperDisable); + handler.item("shared_stepper_reset_pin", _sharedStepperReset); // Handle axis names xyzabc. handler.section is inferred // from a template. diff --git a/FluidNC/src/Machine/Axes.h b/FluidNC/src/Machine/Axes.h index 4dde41836..d4577782b 100644 --- a/FluidNC/src/Machine/Axes.h +++ b/FluidNC/src/Machine/Axes.h @@ -34,6 +34,7 @@ namespace Machine { inline char axisName(int index) { return index < MAX_N_AXIS ? _names[index] : '?'; } // returns axis letter Pin _sharedStepperDisable; + Pin _sharedStepperReset; int _numberAxis = 0; Axis* _axis[MAX_N_AXIS]; From 29004378e795c1b1523d985f19cb6125f1835e29 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Sat, 26 Feb 2022 13:54:55 -1000 Subject: [PATCH 065/100] Tablet UI only goes fullscreen on mobile devices --- FluidNC/data/index.html.gz | Bin 122395 -> 122432 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/FluidNC/data/index.html.gz b/FluidNC/data/index.html.gz index 0a1b68081f3ac61baa07be0ba71695e9ae1bdb06..38bed0f6f454b8e37a55dac3a8e4a84438e78e34 100644 GIT binary patch delta 20089 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 Date: Thu, 24 Feb 2022 14:00:56 -1000 Subject: [PATCH 066/100] Planner and segment buffers are configurable --- FluidNC/src/Machine/MachineConfig.cpp | 1 + FluidNC/src/Machine/MachineConfig.h | 2 ++ FluidNC/src/Main.cpp | 2 ++ FluidNC/src/Planner.cpp | 23 +++++++++++++++-------- FluidNC/src/Planner.h | 5 ++--- FluidNC/src/Stepper.cpp | 23 +++++++++++++++++------ FluidNC/src/Stepper.h | 2 ++ FluidNC/src/StepperPrivate.h | 8 -------- FluidNC/src/Stepping.cpp | 3 +++ FluidNC/src/Stepping.h | 9 +++++++++ 10 files changed, 53 insertions(+), 25 deletions(-) diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index 14aafa767..bb0a3be67 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -56,6 +56,7 @@ namespace Machine { handler.item("report_inches", _reportInches); handler.item("enable_parking_override_control", _enableParkingOverrideControl); handler.item("use_line_numbers", _useLineNumbers); + handler.item("planner_blocks", _planner_blocks); Spindles::SpindleFactory::factory(handler, _spindles); } diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index d78f7fe32..19acb7308 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -74,6 +74,8 @@ namespace Machine { bool _verboseErrors = false; bool _reportInches = false; + size_t _planner_blocks = 128; + // Enables a special set of M-code commands that enables and disables the parking motion. // These are controlled by `M56`, `M56 P1`, or `M56 Px` to enable and `M56 P0` to disable. // The command is modal and will be set after a planner sync. Since it is GCode, it is diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 9417bb7b6..94db30bbb 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -72,6 +72,8 @@ void setup() { config->_stepping->init(); // Configure stepper interrupt timers + plan_init(); + config->_userOutputs->init(); config->_axes->init(); diff --git a/FluidNC/src/Planner.cpp b/FluidNC/src/Planner.cpp index 44b7b8063..062831f0e 100644 --- a/FluidNC/src/Planner.cpp +++ b/FluidNC/src/Planner.cpp @@ -16,11 +16,18 @@ #include // PSoc Required for labs #include -static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions -static uint8_t block_buffer_tail; // Index of the block to process now -static uint8_t block_buffer_head; // Index of the next block to be pushed -static uint8_t next_buffer_head; // Index of the next buffer head -static uint8_t block_buffer_planned; // Index of the optimally planned block +static plan_block_t* block_buffer = nullptr; // A ring buffer for motion instructions +static uint8_t block_buffer_tail; // Index of the block to process now +static uint8_t block_buffer_head; // Index of the next block to be pushed +static uint8_t next_buffer_head; // Index of the next buffer head +static uint8_t block_buffer_planned; // Index of the optimally planned block + +void plan_init() { + if (block_buffer) { + delete[] block_buffer; + } + block_buffer = new plan_block_t[config->_planner_blocks]; +} // Define planner variables typedef struct { @@ -35,7 +42,7 @@ static planner_t pl; // Returns the index of the next block in the ring buffer. Also called by stepper segment buffer. static uint8_t plan_next_block_index(uint8_t block_index) { block_index++; - if (block_index == BLOCK_BUFFER_SIZE) { + if (block_index == config->_planner_blocks) { block_index = 0; } return block_index; @@ -44,7 +51,7 @@ static uint8_t plan_next_block_index(uint8_t block_index) { // Returns the index of the previous block in the ring buffer static uint8_t plan_prev_block_index(uint8_t block_index) { if (block_index == 0) { - block_index = BLOCK_BUFFER_SIZE; + block_index = config->_planner_blocks; } block_index--; return block_index; @@ -435,7 +442,7 @@ void plan_sync_position() { // Called from report_realtime_status uint8_t plan_get_block_buffer_available() { if (block_buffer_head >= block_buffer_tail) { - return (BLOCK_BUFFER_SIZE - 1) - (block_buffer_head - block_buffer_tail); + return (config->_planner_blocks - 1) - (block_buffer_head - block_buffer_tail); } else { return block_buffer_tail - block_buffer_head - 1; } diff --git a/FluidNC/src/Planner.h b/FluidNC/src/Planner.h index f3c1d9af3..09e76998b 100644 --- a/FluidNC/src/Planner.h +++ b/FluidNC/src/Planner.h @@ -15,9 +15,6 @@ #include -// The number of linear motions in the planner buffer to be planned at any give time. -const int BLOCK_BUFFER_SIZE = 16; - // Define planner data condition flags. Used to denote running conditions of a block. struct PlMotion { uint8_t rapidMotion : 1; @@ -80,6 +77,8 @@ struct plan_line_data_t { bool is_jog; // true if this was generated due to a jog command }; +void plan_init(); + // Initialize and reset the motion plan subsystem void plan_reset(); // Reset all void plan_reset_buffer(); // Reset buffer only. diff --git a/FluidNC/src/Stepper.cpp b/FluidNC/src/Stepper.cpp index a8260819b..c87d2d2b4 100644 --- a/FluidNC/src/Stepper.cpp +++ b/FluidNC/src/Stepper.cpp @@ -32,7 +32,7 @@ uint32_t pl_seq0; // Stores the planner block Bresenham algorithm execution data for the segments in the segment // buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will -// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1). +// never exceed the number of accessible stepper buffer segments (config->_stepping->_segments-1). // NOTE: This data is copied from the prepped planner blocks so that the planner blocks may be // discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this // data for its own use. @@ -46,7 +46,7 @@ struct st_block_t { // uint32_t exit[MAX_N_AXIS]; #endif }; -static volatile st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE - 1]; +static volatile st_block_t* st_block_buffer = nullptr; // Primary stepper segment ring buffer. Contains small, short line segments for the stepper // algorithm to execute, which are "checked-out" incrementally from the first block in the @@ -63,7 +63,18 @@ struct segment_t { uint16_t spindle_dev_speed; // Spindle speed scaled to the device SpindleSpeed spindle_speed; // Spindle speed in GCode units }; -static segment_t segment_buffer[SEGMENT_BUFFER_SIZE]; +static segment_t* segment_buffer = nullptr; + +void Stepper::init() { + if (st_block_buffer) { + delete[] st_block_buffer; + } + st_block_buffer = new st_block_t[config->_stepping->_segments - 1]; + if (segment_buffer) { + delete[] segment_buffer; + } + segment_buffer = new segment_t[config->_stepping->_segments]; +} // Stepper ISR data struct. Contains the running data for the main stepper ISR. typedef struct { @@ -293,7 +304,7 @@ void IRAM_ATTR Stepper::pulse_func() { if (st.step_count == 0) { // Segment is complete. Discard current segment and advance segment indexing. st.exec_segment = NULL; - segment_buffer_tail = segment_buffer_tail >= (SEGMENT_BUFFER_SIZE - 1) ? 0 : segment_buffer_tail + 1; + segment_buffer_tail = segment_buffer_tail >= (config->_stepping->_segments - 1) ? 0 : segment_buffer_tail + 1; } config->_axes->unstep(); @@ -382,7 +393,7 @@ void Stepper::parking_restore_buffer() { // Increments the step segment buffer block data ring buffer. static uint8_t next_block_index(uint8_t block_index) { block_index++; - return block_index == (SEGMENT_BUFFER_SIZE - 1) ? 0 : block_index; + return block_index == (config->_stepping->_segments - 1) ? 0 : block_index; } /* Prepares step segment buffer. Continuously called from main program. @@ -773,7 +784,7 @@ void Stepper::prep_buffer() { // Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it. auto lastseg = segment_next_head; - segment_next_head = segment_next_head >= (SEGMENT_BUFFER_SIZE - 1) ? 0 : segment_next_head + 1; + segment_next_head = segment_next_head >= (config->_stepping->_segments - 1) ? 0 : segment_next_head + 1; segment_buffer_head = lastseg; // Update the appropriate planner and segment data. diff --git a/FluidNC/src/Stepper.h b/FluidNC/src/Stepper.h index dac878acf..ce76aa888 100644 --- a/FluidNC/src/Stepper.h +++ b/FluidNC/src/Stepper.h @@ -14,6 +14,8 @@ #include namespace Stepper { + void init(); + void pulse_func(); // Enable steppers, but cycle does not start unless called by motion control or realtime command. diff --git a/FluidNC/src/StepperPrivate.h b/FluidNC/src/StepperPrivate.h index 6b39538a5..081d37828 100644 --- a/FluidNC/src/StepperPrivate.h +++ b/FluidNC/src/StepperPrivate.h @@ -3,14 +3,6 @@ #pragma once -// Governs the size of the intermediary step segment buffer between the step execution algorithm -// and the planner blocks. Each segment is set of steps executed at a constant velocity over a -// fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner -// block velocity profile is traced exactly. The size of this buffer governs how much step -// execution lead time there is for other processes to run -// before having to come back and refill this buffer, currently at ~50msec of step moves. -const int SEGMENT_BUFFER_SIZE = 6; - // Some useful constants. const float DT_SEGMENT = (1.0f / (float(ACCELERATION_TICKS_PER_SECOND) * 60.0f)); // min/segment const float REQ_MM_INCREMENT_SCALAR = 1.25f; diff --git a/FluidNC/src/Stepping.cpp b/FluidNC/src/Stepping.cpp index 1ea3fbee8..f7b18c6a4 100644 --- a/FluidNC/src/Stepping.cpp +++ b/FluidNC/src/Stepping.cpp @@ -31,6 +31,8 @@ namespace Machine { // Register pulse_func with the I2S subsystem // This could be done via the linker. // i2s_out_set_pulse_callback(Stepper::pulse_func); + + Stepper::init(); } void Stepping::reset() { @@ -168,6 +170,7 @@ namespace Machine { handler.item("pulse_us", _pulseUsecs); handler.item("dir_delay_us", _directionDelayUsecs); handler.item("disable_delay_us", _disableDelayUsecs); + handler.item("segments", _segments); } void Stepping::afterParse() { diff --git a/FluidNC/src/Stepping.h b/FluidNC/src/Stepping.h index 99743039e..655dd0058 100644 --- a/FluidNC/src/Stepping.h +++ b/FluidNC/src/Stepping.h @@ -39,6 +39,15 @@ namespace Machine { Stepping() = default; + // _segments is the number of entries in the step segment buffer between the step execution algorithm + // and the planner blocks. Each segment is set of steps executed at a constant velocity over a + // fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner + // block velocity profile is traced exactly. The size of this buffer governs how much step + // execution lead time there is for other processes to run. The latency for a feedhold or other + // override is roughly 10 ms times _segments. + + size_t _segments = 6; + uint8_t _idleMsecs = 255; uint32_t _pulseUsecs = 4; uint32_t _directionDelayUsecs = 0; From 3be79a97a24cbba69bd1312765eae58b4c42e90f Mon Sep 17 00:00:00 2001 From: bdring Date: Mon, 28 Feb 2022 17:46:05 -0600 Subject: [PATCH 067/100] Update MachineConfig.h Default should be 16. A tested value was accidentally left in place. --- FluidNC/src/Machine/MachineConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index 19acb7308..d87181eb1 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -74,7 +74,7 @@ namespace Machine { bool _verboseErrors = false; bool _reportInches = false; - size_t _planner_blocks = 128; + size_t _planner_blocks = 16; // Enables a special set of M-code commands that enables and disables the parking motion. // These are controlled by `M56`, `M56 P1`, or `M56 Px` to enable and `M56 P0` to disable. From e9ac91d599c7d5020c4046aa9e55e1e26f794229 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 11:15:33 +0100 Subject: [PATCH 068/100] Added pin extender support. Made everything compile yet again. --- FluidNC/src/Extenders/Extenders.cpp | 58 ++++ FluidNC/src/Extenders/Extenders.h | 25 ++ FluidNC/src/Extenders/PCA9539.cpp | 271 ++++++++++++++++++ FluidNC/src/Extenders/PCA9539.h | 126 ++++++++ FluidNC/src/Extenders/PinExtender.h | 23 ++ FluidNC/src/Extenders/PinExtenderDriver.cpp | 12 + FluidNC/src/Extenders/PinExtenderDriver.h | 33 +++ FluidNC/src/Machine/I2CBus.cpp | 54 ++++ FluidNC/src/Machine/I2CBus.h | 33 +++ FluidNC/src/Machine/MachineConfig.cpp | 2 + FluidNC/src/Machine/MachineConfig.h | 4 + FluidNC/src/Main.cpp | 8 +- FluidNC/src/MotionControl.cpp | 4 +- FluidNC/src/Pins/ExtPinDetail.cpp | 93 ++++++ FluidNC/src/Pins/ExtPinDetail.h | 43 +++ FluidNC/test/Pins/GPIO.cpp | 2 +- UnitTests.vcxproj | 19 +- UnitTests.vcxproj.filters | 60 +++- X86TestSupport/TestSupport/Wire.cpp | 4 + X86TestSupport/TestSupport/Wire.h | 56 ++++ X86TestSupport/TestSupport/driver/uart.h | 2 + .../TestSupport/freertos/FreeRTOS.h | 2 + 22 files changed, 916 insertions(+), 18 deletions(-) create mode 100644 FluidNC/src/Extenders/Extenders.cpp create mode 100644 FluidNC/src/Extenders/Extenders.h create mode 100644 FluidNC/src/Extenders/PCA9539.cpp create mode 100644 FluidNC/src/Extenders/PCA9539.h create mode 100644 FluidNC/src/Extenders/PinExtender.h create mode 100644 FluidNC/src/Extenders/PinExtenderDriver.cpp create mode 100644 FluidNC/src/Extenders/PinExtenderDriver.h create mode 100644 FluidNC/src/Machine/I2CBus.cpp create mode 100644 FluidNC/src/Machine/I2CBus.h create mode 100644 FluidNC/src/Pins/ExtPinDetail.cpp create mode 100644 FluidNC/src/Pins/ExtPinDetail.h create mode 100644 X86TestSupport/TestSupport/Wire.cpp create mode 100644 X86TestSupport/TestSupport/Wire.h diff --git a/FluidNC/src/Extenders/Extenders.cpp b/FluidNC/src/Extenders/Extenders.cpp new file mode 100644 index 000000000..d2b46821f --- /dev/null +++ b/FluidNC/src/Extenders/Extenders.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" + +namespace Extenders { + PinExtender::PinExtender() : _driver(nullptr) {} + + void PinExtender::validate() const { + if (_driver) { + _driver->validate(); + } + } + void PinExtender::group(Configuration::HandlerBase& handler) { PinExtenderFactory::factory(handler, _driver); } + void PinExtender::init() { + if (_driver) { + _driver->init(); + } + } + + PinExtender::~PinExtender() { delete _driver; } + + Extenders::Extenders() { + for (int i = 0; i < 16; ++i) { + _pinDrivers[i] = nullptr; + } + } + + void Extenders::validate() const {} + + void Extenders::group(Configuration::HandlerBase& handler) { + for (int i = 0; i < 10; ++i) { + char tmp[11 + 3]; + tmp[0] = 0; + strcat(tmp, "pinextender"); + + for (size_t i = 0; i < 10; ++i) { + tmp[11] = char(i + '0'); + tmp[12] = '\0'; + handler.section(tmp, _pinDrivers[i]); + } + } + } + + void Extenders::init() { + for (int i = 0; i < 16; ++i) { + if (_pinDrivers[i] != nullptr) { + _pinDrivers[i]->init(); + } + } + } + + Extenders::~Extenders() { + for (int i = 0; i < 16; ++i) { + delete _pinDrivers[i]; + } + } +} diff --git a/FluidNC/src/Extenders/Extenders.h b/FluidNC/src/Extenders/Extenders.h new file mode 100644 index 000000000..3d83af476 --- /dev/null +++ b/FluidNC/src/Extenders/Extenders.h @@ -0,0 +1,25 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "../Configuration/GenericFactory.h" +#include "PinExtender.h" + +namespace Extenders { + class Extenders : public Configuration::Configurable { + public: + Extenders(); + + PinExtender* _pinDrivers[16]; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + void init(); + + ~Extenders(); + }; + + using PinExtenderFactory = Configuration::GenericFactory; +} diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp new file mode 100644 index 000000000..d7f40f2a1 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" +#include "PCA9539.h" +#include "../Logging.h" + +#include +#include + +namespace Extenders { + void PCA9539::claim(pinnum_t index) { + Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); + + _claimed |= mask; + } + + void PCA9539::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed &= ~mask; + } + + uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + auto err = bus->write(address, ®, 1); + + if (err) { + // log_info("Error writing to i2c bus. Code: " << err); + return 0; + } + + uint8_t inputData; + if (bus->read(address, &inputData, 1) != 1) { + // log_info("Error reading from i2c bus."); + } + + return inputData; + } + + void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + auto err = bus->write(address, data, 2); + + if (err) { + log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); + } + } + + void PCA9539::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); + } + + void PCA9539::group(Configuration::HandlerBase& handler) { + handler.item("interrupt0", _isrData[0]._pin); + handler.item("interrupt1", _isrData[1]._pin); + handler.item("interrupt2", _isrData[2]._pin); + handler.item("interrupt3", _isrData[3]._pin); + } + + void PCA9539::isrTaskLoop(void* arg) { + auto inst = static_cast(arg); + while (true) { + void* ptr; + if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { + ISRData* valuePtr = static_cast(ptr); + // log_info("PCA state change ISR"); + valuePtr->updateValueFromDevice(); + } + } + } + + void PCA9539::init() { + this->_i2cBus = config->_i2c; + + _isrQueue = xQueueCreate(16, sizeof(void*)); + xTaskCreatePinnedToCore(isrTaskLoop, // task + "isr_handler", // name for task + configMINIMAL_STACK_SIZE + 256, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + data._address = uint8_t(0x74 + i); + data._container = this; + data._valueBase = reinterpret_cast(&_value) + i; + + // Update the value first by reading it: + data.updateValueFromDevice(); + + if (!data._pin.undefined()) { + data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + + // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. + data._pin.attachInterrupt(updatePCAState, FALLING, &data); + } else { + // Reset valueBase so we know it's not bound to an ISR: + data._valueBase = nullptr; + } + } + } + + void PCA9539::ISRData::updateValueFromDevice() { + const uint8_t InputReg = 0; + auto i2cBus = _container->_i2cBus; + + auto r1 = I2CGetValue(i2cBus, _address, InputReg); + auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); + uint16_t oldValue = *_valueBase; + uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); + *_valueBase = value; + + if (_hasISR) { + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1) << i; + + if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { + // log_info("State change pin " << i); + switch (_isrMode[i]) { + case RISING: + if ((value & mask) == mask) { + _isrCallback[i](_isrArgument); + } + break; + case FALLING: + if ((value & mask) == 0) { + _isrCallback[i](_isrArgument); + } + break; + case CHANGE: + _isrCallback[i](_isrArgument); + break; + } + } + } + } + } + + void PCA9539::updatePCAState(void* ptr) { + ISRData* valuePtr = static_cast(ptr); + + BaseType_t xHigherPriorityTaskWoken = false; + xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); + } + + void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { + bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); + bool output = attr.has(Pins::PinAttributes::Output); + + uint64_t mask = uint64_t(1) << index; + _invert = (_invert & ~mask) | (activeLow ? mask : 0); + _configuration = (_configuration & ~mask) | (output ? 0 : mask); + + const uint8_t deviceId = index / 16; + + const uint8_t ConfigReg = 6; + uint8_t address = 0x74 + deviceId; + + uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); + uint8_t reg = ConfigReg + ((index / 8) & 1); + + // log_info("Setup reg " << int(reg) << " with value " << int(value)); + + I2CSetValue(_i2cBus, address, reg, value); + } + + void PCA9539::writePin(pinnum_t index, bool high) { + uint64_t mask = uint64_t(1) << index; + uint64_t oldVal = _value; + uint64_t newVal = high ? mask : uint64_t(0); + _value = (_value & ~mask) | newVal; + + _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); + } + + bool PCA9539::readPin(pinnum_t index) { + uint8_t reg = uint8_t(index / 8); + uint8_t deviceId = reg / 2; + + // If it's handled by the ISR, we don't need to read anything from the device. + // Otherwise, we do. Check: + if (_isrData[deviceId]._valueBase == nullptr) { + const uint8_t InputReg = 0; + uint8_t address = 0x74 + deviceId; + + auto readReg = InputReg + (reg & 1); + auto value = I2CGetValue(_i2cBus, address, readReg); + uint64_t newValue = uint64_t(value) << (int(reg) * 8); + uint64_t mask = uint64_t(0xff) << (int(reg) * 8); + + _value = ((newValue ^ _invert) & mask) | (_value & ~mask); + + // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } + // else { + // log_info("No read, value is " << int(_value)); + // } + + return (_value & (1ull << index)) != 0; + } + + void PCA9539::flushWrites() { + uint64_t write = _value ^ _invert; + for (int i = 0; i < 8; ++i) { + if ((_dirtyRegisters & (1 << i)) != 0) { + const uint8_t OutputReg = 2; + uint8_t address = 0x74 + (i / 2); + + uint8_t val = uint8_t(write >> (8 * i)); + uint8_t reg = OutputReg + (i & 1); + I2CSetValue(_i2cBus, address, reg, val); + } + } + + _dirtyRegisters = 0; + } + + // ISR's: + void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + int device = index / 16; + int pinNumber = index % 16; + + Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); + + _isrData[device]._isrCallback[pinNumber] = callback; + _isrData[device]._isrArgument[pinNumber] = arg; + _isrData[device]._isrMode[pinNumber] = mode; + _isrData[device]._hasISR = true; + } + + void PCA9539::detachInterrupt(pinnum_t index) { + int device = index / 16; + int pinNumber = index % 16; + + _isrData[device]._isrCallback[pinNumber] = nullptr; + _isrData[device]._isrArgument[pinNumber] = nullptr; + _isrData[device]._isrMode[pinNumber] = 0; + + bool hasISR = false; + for (int i = 0; i < 16; ++i) { + hasISR |= (_isrData[device]._isrArgument[i] != nullptr); + } + _isrData[device]._hasISR = hasISR; + } + + const char* PCA9539::name() const { return "pca9539"; } + + PCA9539 ::~PCA9539() { + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + if (!data._pin.undefined()) { + data._pin.detachInterrupt(); + } + } + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("pca9539"); + } +} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h new file mode 100644 index 000000000..d90068370 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.h @@ -0,0 +1,126 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + class PCA9539 : public PinExtenderDriver { + friend class Pins::PCA9539PinDetail; + + // Address can be set for up to 4 devices. Each device supports 16 pins. + + static const int numberPins = 16 * 4; + uint64_t _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Registers: + // 4x16 = 64 bits. Fits perfectly into an uint64. + uint64_t _configuration = 0; + uint64_t _invert = 0; + volatile uint64_t _value = 0; + + // 4 devices, 2 registers per device. 8 bits is enough: + uint8_t _dirtyRegisters = 0; + + QueueHandle_t _isrQueue = nullptr; + TaskHandle_t _isrHandler = nullptr; + + static void isrTaskLoop(void* arg); + + struct ISRData { + ISRData() = default; + + Pin _pin; + PCA9539* _container = nullptr; + volatile uint16_t* _valueBase = nullptr; + uint8_t _address = 0; + + typedef void (*ISRCallback)(void*); + + bool _hasISR = false; + ISRCallback _isrCallback[16] = { 0 }; + void* _isrArgument[16] = { 0 }; + int _isrMode[16] = { 0 }; + + void IRAM_ATTR updateValueFromDevice(); + }; + + ISRData _isrData[4]; + static void IRAM_ATTR updatePCAState(void* ptr); + + public: + PCA9539() = default; + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~PCA9539(); + }; +} diff --git a/FluidNC/src/Extenders/PinExtender.h b/FluidNC/src/Extenders/PinExtender.h new file mode 100644 index 000000000..2c60327c1 --- /dev/null +++ b/FluidNC/src/Extenders/PinExtender.h @@ -0,0 +1,23 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "PinExtenderDriver.h" + +namespace Extenders { + class PinExtender : public Configuration::Configurable { + public: + // Other configurations? + PinExtenderDriver* _driver; + + PinExtender(); + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + void init(); + + ~PinExtender(); + }; +} diff --git a/FluidNC/src/Extenders/PinExtenderDriver.cpp b/FluidNC/src/Extenders/PinExtenderDriver.cpp new file mode 100644 index 000000000..efc602acc --- /dev/null +++ b/FluidNC/src/Extenders/PinExtenderDriver.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "PinExtenderDriver.h" + +namespace Extenders { + + void PinExtenderDriver::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + Assert(false, "Interrupts are not supported by pin extender for pin %d", index); + } + void PinExtenderDriver::detachInterrupt(pinnum_t index) { Assert(false, "Interrupts are not supported by pin extender"); } +} diff --git a/FluidNC/src/Extenders/PinExtenderDriver.h b/FluidNC/src/Extenders/PinExtenderDriver.h new file mode 100644 index 000000000..dfc4e79ad --- /dev/null +++ b/FluidNC/src/Extenders/PinExtenderDriver.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "../Pins/PinAttributes.h" + +#include "../Platform.h" + +namespace Extenders { + class PinExtenderDriver : public Configuration::Configurable { + public: + virtual void init() = 0; + + virtual void claim(pinnum_t index) = 0; + virtual void free(pinnum_t index) = 0; + + virtual void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) = 0; + virtual void IRAM_ATTR writePin(pinnum_t index, bool high) = 0; + virtual bool IRAM_ATTR readPin(pinnum_t index) = 0; + virtual void IRAM_ATTR flushWrites() = 0; + + virtual void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode); + virtual void detachInterrupt(pinnum_t index); + + // Name is required for the configuration factory to work. + virtual const char* name() const = 0; + + // Virtual base classes require a virtual destructor. + virtual ~PinExtenderDriver() {} + }; +} diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp new file mode 100644 index 000000000..efc6dded3 --- /dev/null +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "I2CBus.h" + +namespace Machine { + void I2CBus::validate() const { + if (_sda.defined() || _scl.defined()) { + Assert(_sda.defined(), "I2C SDA pin should be configured once"); + Assert(_scl.defined(), "I2C SCL pin should be configured once"); + Assert(_busNumber == 0 || _busNumber == 1, "The ESP32 only has 2 I2C buses. Number %d is invalid", _busNumber); + } + } + + void I2CBus::group(Configuration::HandlerBase& handler) { + handler.item("sda", _sda); + handler.item("scl", _scl); + handler.item("busNumber", _busNumber); + handler.item("frequency", _frequency); + } + + void I2CBus::init() { + log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); + + auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + + if (_busNumber == 0) { + i2c = &Wire; + } else { + i2c = &Wire1; + } + i2c->begin(sdaPin, sclPin /*, _frequency */); + } + + int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { + i2c->beginTransmission(address); + for (size_t i = 0; i < count; ++i) { + i2c->write(data[i]); + } + return i2c->endTransmission(); // i2c_err_t ?? + } + + int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { + for (size_t i = 0; i < count; ++i) { + if (i2c->requestFrom((int)address, 1) != 1) { + return i; + } + data[i] = i2c->read(); + } + return count; + } + +} diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h new file mode 100644 index 000000000..a55673ae5 --- /dev/null +++ b/FluidNC/src/Machine/I2CBus.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" + +#include +#include + +namespace Machine { + class I2CBus : public Configuration::Configurable { + protected: + TwoWire* i2c; + + public: + I2CBus() = default; + + int _busNumber = 0; + Pin _sda; + Pin _scl; + uint32_t _frequency = 0; + + void init(); + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! + int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); + + ~I2CBus() = default; + }; +} diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index bb0a3be67..377b64929 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -40,12 +40,14 @@ namespace Machine { handler.section("axes", _axes); handler.section("kinematics", _kinematics); handler.section("i2so", _i2so); + handler.section("i2c", _i2c); handler.section("spi", _spi); handler.section("sdcard", _sdCard); handler.section("control", _control); handler.section("coolant", _coolant); handler.section("probe", _probe); handler.section("macros", _macros); + handler.section("extenders", _extenders); handler.section("start", _start); handler.section("user_outputs", _userOutputs); diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index d87181eb1..be8eb7f9c 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -11,6 +11,7 @@ #include "../CoolantControl.h" #include "../Kinematics/Kinematics.h" #include "../WebUI/BTConfig.h" +#include "../Extenders/Extenders.h" #include "../Control.h" #include "../Probe.h" #include "../SDCard.h" @@ -21,6 +22,7 @@ #include "../Config.h" #include "Axes.h" #include "SPIBus.h" +#include "I2CBus.h" #include "I2SOBus.h" #include "UserOutputs.h" #include "Macros.h" @@ -58,6 +60,7 @@ namespace Machine { Axes* _axes = nullptr; Kinematics* _kinematics = nullptr; SPIBus* _spi = nullptr; + I2CBus* _i2c = nullptr; I2SOBus* _i2so = nullptr; Stepping* _stepping = nullptr; CoolantControl* _coolant = nullptr; @@ -68,6 +71,7 @@ namespace Machine { Macros* _macros = nullptr; Start* _start = nullptr; Spindles::SpindleList _spindles; + Extenders::Extenders* _extenders = nullptr; float _arcTolerance = 0.002f; float _junctionDeviation = 0.01f; diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 94db30bbb..b0fe7e696 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -69,17 +69,19 @@ void setup() { config->_sdCard->init(); } } + if (config->_i2c) { + config->_i2c->init(); + } + + // TODO FIXME: Initialize extenders *here* config->_stepping->init(); // Configure stepper interrupt timers plan_init(); config->_userOutputs->init(); - config->_axes->init(); - config->_control->init(); - config->_kinematics->init(); memset(motor_steps, 0, sizeof(motor_steps)); // Clear machine position. diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index ee11f267b..7c2caa7f0 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -132,7 +132,7 @@ void mc_arc(float* target, auto n_axis = config->_axes->_numberAxis; - float previous_position[n_axis] = { 0.0 }; + float previous_position[MAX_N_AXIS] = { 0.0f }; for (size_t i = 0; i < n_axis; i++) { previous_position[i] = position[i]; } @@ -164,7 +164,7 @@ void mc_arc(float* target, pl_data->motion.inverseTime = 0; // Force as feed absolute mode over arc segments. } float theta_per_segment = angular_travel / segments; - float linear_per_segment[n_axis]; + float linear_per_segment[MAX_N_AXIS]; linear_per_segment[axis_linear] = (target[axis_linear] - position[axis_linear]) / segments; for (size_t i = A_AXIS; i < n_axis; i++) { linear_per_segment[i] = (target[i] - position[i]) / segments; diff --git a/FluidNC/src/Pins/ExtPinDetail.cpp b/FluidNC/src/Pins/ExtPinDetail.cpp new file mode 100644 index 000000000..a5809f1b0 --- /dev/null +++ b/FluidNC/src/Pins/ExtPinDetail.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "ExtPinDetail.h" + +namespace Pins { + ExtPinDetail::ExtPinDetail(int device, pinnum_t index, const PinOptionsParser& options) : + PinDetail(index), _device(device), _capabilities(PinCapabilities::Output | PinCapabilities::Input | PinCapabilities::ISR), + _attributes(Pins::PinAttributes::Undefined) { + // User defined pin capabilities + for (auto opt : options) { + if (opt.is("low")) { + _attributes = _attributes | PinAttributes::ActiveLow; + } else if (opt.is("high")) { + // Default: Active HIGH. + } else { + Assert(false, "Unsupported I2SO option '%s'", opt()); + } + } + } + + PinCapabilities ExtPinDetail::capabilities() const { return PinCapabilities::Input | PinCapabilities::Output | PinCapabilities::ISR; } + + // I/O: + void ExtPinDetail::write(int high) { + Assert(_owner != nullptr, "Cannot write to uninitialized pin"); + _owner->writePin(_index, high); + } + + void ExtPinDetail::synchronousWrite(int high) { + Assert(_owner != nullptr, "Cannot write to uninitialized pin"); + _owner->writePin(_index, high); + _owner->flushWrites(); + } + + int ExtPinDetail::read() { + Assert(_owner != nullptr, "Cannot read from uninitialized pin"); + return _owner->readPin(_index); + } + + void ExtPinDetail::setAttr(PinAttributes value) { + // We setup the driver in setAttr. Before this time, the owner might not be valid. + + // Check the attributes first: + Assert(value.has(PinAttributes::Input) || value.has(PinAttributes::Output), "PCA9539 pins can be used as either input or output."); + Assert(value.has(PinAttributes::Input) != value.has(PinAttributes::Output), "PCA9539 pins can be used as either input or output."); + Assert(value.validateWith(this->_capabilities), "Requested attributes do not match the PCA9539 pin capabilities."); + Assert(!_attributes.conflictsWith(value), "Attributes on this pin have been set before, and there's a conflict."); + + _attributes = value; + + bool activeLow = _attributes.has(PinAttributes::ActiveLow); + + if (_owner == nullptr) { + auto ext = config->_extenders; + if (ext != nullptr && ext->_pinDrivers[_device] != nullptr && ext->_pinDrivers[_device]->_driver != nullptr) { + _owner = ext->_pinDrivers[_device]->_driver; + } else { + Assert(false, "Cannot find pin extender definition in configuration for pin pinext%d.%d", _device, _index); + } + + _owner->claim(_index); + } + + _owner->setupPin(_index, _attributes); + _owner->writePin(_index, value.has(PinAttributes::InitialOn)); + } + + PinAttributes ExtPinDetail::getAttr() const { return _attributes; } + + void ExtPinDetail::attachInterrupt(void (*callback)(void*), void* arg, int mode) { + Assert(_owner != nullptr, "Cannot attach ISR on uninitialized pin"); + _owner->attachInterrupt(_index, callback, arg, mode); + } + void ExtPinDetail::detachInterrupt() { + Assert(_owner != nullptr, "Cannot detach ISR on uninitialized pin"); + _owner->detachInterrupt(_index); + } + + String ExtPinDetail::toString() { + auto s = String("pinext") + int(_device) + String(".") + int(_index); + if (_attributes.has(PinAttributes::ActiveLow)) { + s += ":low"; + } + return s; + } + + ExtPinDetail::~ExtPinDetail() { + if (_owner) { + _owner->free(_index); + } + } +} diff --git a/FluidNC/src/Pins/ExtPinDetail.h b/FluidNC/src/Pins/ExtPinDetail.h new file mode 100644 index 000000000..df1f9320f --- /dev/null +++ b/FluidNC/src/Pins/ExtPinDetail.h @@ -0,0 +1,43 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinDetail.h" +#include "../Extenders/PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" + +#include + +namespace Pins { + class ExtPinDetail : public PinDetail { + Extenders::PinExtenderDriver* _owner = nullptr; + int _device; + + PinCapabilities _capabilities; + PinAttributes _attributes; + + public: + ExtPinDetail(int device, pinnum_t index, const PinOptionsParser& options); + + PinCapabilities capabilities() const override; + + // I/O: + void write(int high) override; + void synchronousWrite(int high) override; + int read() override; + + // ISR's: + void attachInterrupt(void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt() override; + + void setAttr(PinAttributes value) override; + PinAttributes getAttr() const override; + + String toString() override; + + ~ExtPinDetail() override; + }; +} diff --git a/FluidNC/test/Pins/GPIO.cpp b/FluidNC/test/Pins/GPIO.cpp index c17acdde3..39ec3d49d 100644 --- a/FluidNC/test/Pins/GPIO.cpp +++ b/FluidNC/test/Pins/GPIO.cpp @@ -301,7 +301,7 @@ namespace Pins { hitCount = 0; int expected = 0; - gpio16.attachInterrupt(this, mode); + // gpio16.attachInterrupt(this, mode); // Two ways to set I/O: // 1. using on/off diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 1f9d64333..93d739c8d 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -33,8 +33,15 @@ + + + + + + + @@ -97,7 +104,6 @@ - @@ -206,10 +212,17 @@ + + + + + + + @@ -223,6 +236,7 @@ + @@ -240,7 +254,6 @@ - @@ -305,7 +318,6 @@ - @@ -347,6 +359,7 @@ + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 34ac5140a..d8f5ac631 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -46,6 +46,9 @@ {dc4ba3bc-4342-4a67-aea2-f68877f1ee69} + + {2b9b9202-4bb3-450e-a90b-99f042de0af2} + @@ -225,9 +228,6 @@ src\Configuration - - src - src\Motors @@ -573,6 +573,30 @@ X86TestSupport + + X86TestSupport + + + src + + + src\Machine + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + src\Pins + @@ -665,9 +689,6 @@ src\Configuration - - src - src\StackTrace @@ -815,9 +836,6 @@ src\Spindles - - src - src\Machine @@ -989,6 +1007,30 @@ X86TestSupport + + src + + + src + + + src\Machine + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + X86TestSupport + + + src\Pins + diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp new file mode 100644 index 000000000..046c93eda --- /dev/null +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -0,0 +1,4 @@ +#include "Wire.h" + +TwoWire Wire(0); +TwoWire Wire1(1); diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h new file mode 100644 index 000000000..aa9f01dcf --- /dev/null +++ b/X86TestSupport/TestSupport/Wire.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include "Stream.h" + +class TwoWire : public Stream { +public: + TwoWire(uint8_t bus_num) {} + ~TwoWire() {} + + //call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl) {} + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } + bool end() { return true; } + + void setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms + uint16_t getTimeOut() { return 0; } + + bool setClock(uint32_t) {} + uint32_t getClock() { return 0; } + + void beginTransmission(uint16_t address) {} + void beginTransmission(uint8_t address) {} + void beginTransmission(int address) {} + + uint8_t endTransmission(bool sendStop) { return 0; } + uint8_t endTransmission(void) { return 0; } + + size_t requestFrom(uint16_t address, size_t size, bool sendStop) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { return 0; } + size_t requestFrom(uint8_t address, size_t len, bool stopBit) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t size) { return 0; } + uint8_t requestFrom(int address, int size, int sendStop) { return 0; } + uint8_t requestFrom(int address, int size) { return 0; } + + size_t write(uint8_t) { return 0; } + size_t write(const uint8_t*, size_t) { return 0; } + int available(void) { return 0; } + int read(void) { return 0; } + int peek(void) { return 0; } + void flush(void) {} + + inline size_t write(const char* s) { return write((uint8_t*)s, strlen(s)); } + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } +}; + +extern TwoWire Wire; +extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/driver/uart.h b/X86TestSupport/TestSupport/driver/uart.h index 9b7192685..8efa6add0 100644 --- a/X86TestSupport/TestSupport/driver/uart.h +++ b/X86TestSupport/TestSupport/driver/uart.h @@ -77,6 +77,8 @@ typedef struct { bool use_ref_tick; /*!< Set to true if UART should be clocked from REF_TICK */ } uart_config_t; +const int UART_FIFO_LEN = 128; + esp_err_t uart_flush(uart_port_t uart_num); esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t* uart_config); esp_err_t uart_driver_install( diff --git a/X86TestSupport/TestSupport/freertos/FreeRTOS.h b/X86TestSupport/TestSupport/freertos/FreeRTOS.h index 6b20e1612..11df765a5 100644 --- a/X86TestSupport/TestSupport/freertos/FreeRTOS.h +++ b/X86TestSupport/TestSupport/freertos/FreeRTOS.h @@ -29,3 +29,5 @@ inline void vTaskEnterCritical(portMUX_TYPE* mux) { inline int32_t xPortGetFreeHeapSize() { return 1024 * 1024 * 4; } + +#define configMINIMAL_STACK_SIZE 768 From e2fcd5d098ab1a83beb22be7301e45db58f65bd5 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 17:03:09 +0100 Subject: [PATCH 069/100] Added some test code to first check if the pin extender works. Seems to be fine. --- FluidNC/src/Extenders/PCA9539.cpp | 23 ++++---- FluidNC/src/Machine/I2CBus.cpp | 12 +++- FluidNC/src/Machine/I2CBus.h | 19 ++++++- FluidNC/src/Main.cpp | 94 ++++++++++++++++++++++++++++++- FluidNC/src/Pin.cpp | 11 ++++ 5 files changed, 143 insertions(+), 16 deletions(-) diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp index d7f40f2a1..eea3cb056 100644 --- a/FluidNC/src/Extenders/PCA9539.cpp +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -24,22 +24,26 @@ namespace Extenders { } uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); + auto err = bus->write(address, ®, 1); if (err) { - // log_info("Error writing to i2c bus. Code: " << err); + log_info("Error writing to i2c bus. Code: " << err); return 0; } uint8_t inputData; if (bus->read(address, &inputData, 1) != 1) { - // log_info("Error reading from i2c bus."); + log_info("Error reading from i2c bus."); } return inputData; } void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); + uint8_t data[2]; data[0] = reg; data[1] = uint8_t(value); @@ -68,7 +72,7 @@ namespace Extenders { void* ptr; if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { ISRData* valuePtr = static_cast(ptr); - // log_info("PCA state change ISR"); + log_info("PCA state change ISR"); valuePtr->updateValueFromDevice(); } } @@ -80,7 +84,7 @@ namespace Extenders { _isrQueue = xQueueCreate(16, sizeof(void*)); xTaskCreatePinnedToCore(isrTaskLoop, // task "isr_handler", // name for task - configMINIMAL_STACK_SIZE + 256, // size of task stack + configMINIMAL_STACK_SIZE + 512, // size of task stack this, // parameters 1, // priority &_isrHandler, @@ -124,7 +128,7 @@ namespace Extenders { uint16_t mask = uint16_t(1) << i; if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { - // log_info("State change pin " << i); + log_info("State change pin " << i); switch (_isrMode[i]) { case RISING: if ((value & mask) == mask) { @@ -168,7 +172,7 @@ namespace Extenders { uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); uint8_t reg = ConfigReg + ((index / 8) & 1); - // log_info("Setup reg " << int(reg) << " with value " << int(value)); + log_info("Setup reg " << int(reg) << " with value " << int(value)); I2CSetValue(_i2cBus, address, reg, value); } @@ -199,11 +203,10 @@ namespace Extenders { _value = ((newValue ^ _invert) & mask) | (_value & ~mask); - // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } else { + log_info("No read, value is " << int(_value)); } - // else { - // log_info("No read, value is " << int(_value)); - // } return (_value & (1ull << index)) != 0; } diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index efc6dded3..7158661de 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -3,6 +3,8 @@ #include "I2CBus.h" +#include + namespace Machine { void I2CBus::validate() const { if (_sda.defined() || _scl.defined()) { @@ -20,8 +22,6 @@ namespace Machine { } void I2CBus::init() { - log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); - auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); @@ -31,9 +31,14 @@ namespace Machine { i2c = &Wire1; } i2c->begin(sdaPin, sclPin /*, _frequency */); + + log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); } int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { + // log_info("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); + i2c->beginTransmission(address); for (size_t i = 0; i < count; ++i) { i2c->write(data[i]); @@ -42,6 +47,9 @@ namespace Machine { } int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { + // log_info("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); + for (size_t i = 0; i < count; ++i) { if (i2c->requestFrom((int)address, 1) != 1) { return i; diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index a55673ae5..febbf51fc 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -5,13 +5,14 @@ #include "../Configuration/Configurable.h" -#include #include +class TwoWire; + namespace Machine { class I2CBus : public Configuration::Configurable { protected: - TwoWire* i2c; + TwoWire* i2c = nullptr; public: I2CBus() = default; @@ -25,6 +26,20 @@ namespace Machine { void validate() const override; void group(Configuration::HandlerBase& handler) override; + /* + typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN + } i2c_err_t; + */ + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index b0fe7e696..06e92cd44 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -28,6 +28,51 @@ extern void make_user_commands(); +// FOR TESTING: + +# include +# include + +extern "C" void __pinMode(pinnum_t pin, uint8_t mode); + +uint8_t I2CGetValue(uint8_t address, uint8_t reg) { + Wire.beginTransmission(address); + Wire.write(reg); + auto err = Wire.endTransmission(); // i2c_err_t + + if (Wire.requestFrom((int)address, 1) != 1) { + Uart0.println("Error reading from i2c bus."); + return 0; + } + uint8_t result = Wire.read(); + return result; +} + +void I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + + Wire.beginTransmission(address); + for (size_t i = 0; i < 2; ++i) { + Wire.write(data[i]); + } + auto err = Wire.endTransmission(); // i2c_err_t ?? + + if (err) { + Uart0.println("Error writing to i2c bus; PCA9539 failed. Code: "); + Uart0.println(int(err)); + } +} + +volatile bool fired = false; + +void isrHandler() { + fired = true; +} + +// --- Until here. + void setup() { try { uartInit(); // Setup serial port @@ -38,6 +83,49 @@ void setup() { WebUI::WiFiConfig::reset(); + /* TEST STUFF! */ + + /* + // THIS WORKS: + { + Uart0.println("Basic test of pin extender."); + // Wire.begin(sda , scl, frequency); + Wire.begin(13, 14, 100000); + + Uart0.println("Setup pins:"); + + // 1. Setup pins: + I2CSetValue(0x74, 6, 0xFF); // All input pins + I2CSetValue(0x74, 7, 0xFF); // All input pins + + __pinMode(36, INPUT); + attachInterrupt(36, isrHandler, CHANGE); + + // 2. Read input register: + Uart0.println("Main loop:"); + while (true) { + auto r1 = I2CGetValue(0x74, 0); + auto r2 = I2CGetValue(0x74, 1); + uint16_t v = (uint16_t(r1) << 8) | uint16_t(r2); + Uart0.print("Status: "); + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1 << i); + Uart0.print((v & mask) ? '1' : '0'); + } + + if (fired) { + Uart0.print(" ** ISR"); + fired = false; + } + + Uart0.println(); + + delay(1000); + } + } + */ + /* END TEST STUFF */ + display_init(); // Load settings from non-volatile storage @@ -73,7 +161,10 @@ void setup() { config->_i2c->init(); } - // TODO FIXME: Initialize extenders *here* + // We have to initialize the extenders first, before pins are used + if (config->_extenders) { + config->_extenders->init(); + } config->_stepping->init(); // Configure stepper interrupt timers @@ -119,7 +210,6 @@ void setup() { config->_coolant->init(); config->_probe->init(); } - } catch (const AssertionFailed& ex) { // This means something is terribly broken: log_error("Critical error in main_init: " << ex.what()); diff --git a/FluidNC/src/Pin.cpp b/FluidNC/src/Pin.cpp index 0f825ff76..0cbee91ce 100644 --- a/FluidNC/src/Pin.cpp +++ b/FluidNC/src/Pin.cpp @@ -10,6 +10,7 @@ #include "Pins/VoidPinDetail.h" #include "Pins/I2SOPinDetail.h" #include "Pins/ErrorPinDetail.h" +#include "Pins/ExtPinDetail.h" #include // snprintf() Pins::PinDetail* Pin::undefinedPin = new Pins::VoidPinDetail(); @@ -94,6 +95,16 @@ const char* Pin::parse(StringRange tmp, Pins::PinDetail*& pinImplementation) { pinImplementation = new Pins::VoidPinDetail(); } + if (prefix.startsWith("pinext")) { + if (prefix.length() == 7 && prefix[6] >= '0' && prefix[6] <= '9') { + auto deviceId = prefix[6] - '0'; + pinImplementation = new Pins::ExtPinDetail(deviceId, pinnum_t(pinNumber), parser); + } else { + // For now this should be sufficient, if not we can easily change it to 100 extenders: + return "Incorrect pin extender specification. Expected 'pinext[0-9].[port number]'."; + } + } + if (pinImplementation == nullptr) { log_error("Unknown prefix:" << prefix); return "Unknown pin prefix"; From 84a929649bdadf20e3f40097c1f4ee0a19b55bb3 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Wed, 2 Mar 2022 14:50:21 +0100 Subject: [PATCH 070/100] Rewrite of I2C extender code. --- FluidNC/src/Extenders/I2CExtender.cpp | 408 +++++++++++++++++++++ FluidNC/src/Extenders/I2CExtender.h | 176 +++++++++ FluidNC/src/Extenders/PCA9539.cpp | 274 -------------- FluidNC/src/Extenders/PCA9539.h | 126 ------- FluidNC/src/Machine/I2CBus.cpp | 41 ++- FluidNC/src/Machine/I2CBus.h | 20 +- UnitTests.vcxproj | 5 +- UnitTests.vcxproj.filters | 15 +- X86TestSupport/TestSupport/Wire.h | 1 + X86TestSupport/TestSupport/esp32-hal-i2c.h | 13 + 10 files changed, 648 insertions(+), 431 deletions(-) create mode 100644 FluidNC/src/Extenders/I2CExtender.cpp create mode 100644 FluidNC/src/Extenders/I2CExtender.h delete mode 100644 FluidNC/src/Extenders/PCA9539.cpp delete mode 100644 FluidNC/src/Extenders/PCA9539.h create mode 100644 X86TestSupport/TestSupport/esp32-hal-i2c.h diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp new file mode 100644 index 000000000..8a824a24f --- /dev/null +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -0,0 +1,408 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "I2CExtender.h" + +#include "Extenders.h" +#include "../Logging.h" +#include "../Assert.h" +#include "../Machine/I2CBus.h" + +#include +#include +#include + +namespace Extenders { + I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} + + uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + int err; + if ((err = bus->write(address, ®, 1)) != 0) { + log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + return 0; + } else { + uint8_t result = 0; + if (bus->read(address, &result, 1) != 1) { + log_warn("Cannot read from I2C bus: " + << "no response"); + } + return result; + } + } + void I2CExtender::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + + int err = bus->write(address, data, 2); + + if (err) { + log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + } + } + + void I2CExtender::isrTaskLoopDetail() { + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + int registersPerDevice = _ports / 8; + int claimedValues = 0; + uint8_t commonStatus = _operation; + + // Update everything the first operation + _status = 1; + if (_outputReg != 0xFF) { + _status |= 4; // writes + } + if (_inputReg != 0xFF) { + _status |= 8; // reads + } + + // Main loop for I2C handling: + while (true) { + uint8_t newStatus = 0; + + newStatus = _status.exchange(newStatus); + newStatus |= commonStatus; + + if (newStatus != 0) { + if ((newStatus & 2) != 0) { + break; + } + + // Update config: + if ((newStatus & 1) != 0) { + // First fence! + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + + // Configuration dirty. Update _configuration and _invert. + // + // First check how many u8's are claimed: + claimedValues = 0; + for (int i = 0; i < 8; ++i) { + if (_claimed.bytes[i] != 0) { + claimedValues = i; + } + } + // Configuration: + { + for (int i = 0; i < claimedValues; ++i) { + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + + uint8_t by = _configuration.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + + currentRegister++; + if (currentRegister == registersPerDevice + _operationReg) { + ++address; + } + } + } + // Invert: + if (_invertReg != 0xFF) { + uint8_t currentRegister = _invertReg; + uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { + uint8_t by = _invert.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + + currentRegister++; + if (currentRegister == registersPerDevice + _invertReg) { + ++address; + } + } + } + + // Configuration changed. Writes and reads must be updated. + if (_outputReg != 0xFF) { + newStatus |= 4; // writes + } + if (_inputReg != 0xFF) { + newStatus |= 8; // reads + } + + commonStatus = _operation; + } + + // Handle writes: + if ((newStatus & 4) != 0) { + uint8_t currentRegister = _outputReg; + uint8_t address = _address; + + bool handleInvertSoftware = (_invertReg == 0xFF); + + auto toWrite = _dirtyWrite.exchange(0); + for (int i = 0; i < claimedValues; ++i) { + if ((toWrite & (1 << i)) != 0) { + uint8_t by = handleInvertSoftware ? (_output.bytes[i] ^ _invert.bytes[i]) : _output.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + } + + currentRegister++; + if (currentRegister == registersPerDevice + _outputReg) { + ++address; + } + } + } + + // Handle reads: + if ((newStatus & 8) != 0) { + uint8_t currentRegister = _inputReg; + uint8_t address = _address; + + // If we don't have an ISR, we must update everything. Otherwise, we can cherry pick: + bool handleInvertSoftware = (_invertReg == 0xFF); + + for (int i = 0; i < claimedValues; ++i) { + auto oldByte = _input.bytes[i]; + auto newByte = I2CGetValue(this->_i2cBus, address, currentRegister); + if (handleInvertSoftware) { + newByte ^= _invert.bytes[i]; + } + + if (oldByte != newByte) { + // Handle ISR's: + _input.bytes[i] = newByte; + int offset = claimedValues * 8; + for (int j = 0; j < 8; ++j) { + auto isr = _isrData[offset + j]; + if (isr.defined()) { + auto mask = uint8_t(1 << j); + auto o = (oldByte & mask); + auto n = (newByte & mask); + if (o != n) { + isr.callback(isr.data); + } + } + } + } + + currentRegister++; + if (currentRegister == registersPerDevice + _invertReg) { + ++address; + } + } + } + } + + vTaskDelay(TaskDelayBetweenIterations); + } + } + + void I2CExtender::isrTaskLoop(void* arg) { static_cast(arg)->isrTaskLoopDetail(); } + + void I2CExtender::claim(pinnum_t index) { + Assert(index >= 0 && index < 64, "I2CExtender IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed.value & mask) == 0, "I2CExtender IO port %d is already used.", index); + + _claimed.value |= mask; + } + + void I2CExtender::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed.value &= ~mask; + } + + void I2CExtender::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "I2CExtender works through I2C, but I2C is not configured."); + + // We cannot validate _i2cBus, because that's initialized during `init`. + Assert(_device != int(I2CExtenderDevice::Unknown), "I2C device type is unknown. Cannot continue initializing extender."); + } + + void I2CExtender::group(Configuration::HandlerBase& handler) { + // device: pca9539 + // device_id: 0 + // interrupt: gpio.36 + handler.item("device", _device); + handler.item("device_id", _deviceId); + handler.item("interrupt", _interruptPin); + } + + void I2CExtender::interruptHandler(void* arg) { + auto ext = static_cast(arg); + ext->_status |= 8; + } + + void I2CExtender::init() { + Assert(_isrHandler == nullptr, "Init has already been called on I2C extender."); + + switch (I2CExtenderDevice(_device)) { + case I2CExtenderDevice::PCA9539: + // See data sheet page 7+: + _address = 0x74 + _deviceId; + _ports = 16; + _inputReg = 0; + _outputReg = 2; + _invertReg = 4; + _operationReg = 6; + break; + + default: + Assert(false, "Pin extender device is not supported!"); + break; + } + + xTaskCreatePinnedToCore(isrTaskLoop, // task + "i2cHandler", // name for task + configMINIMAL_STACK_SIZE + 512, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + if (_interruptPin.defined()) { + _interruptPin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + _interruptPin.attachInterrupt(interruptHandler, FALLING, this); + } + } + + void IRAM_ATTR I2CExtender::setupPin(pinnum_t index, Pins::PinAttributes attr) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + _usedIORegisters |= uint8_t(1 << (index / 8)); + + uint64_t mask = 1ull << index; + + if (attr.has(Pins::PinAttributes::Input)) { + _configuration.value |= mask; + + if (attr.has(Pins::PinAttributes::PullUp)) { + _output.value |= mask; + } else if (attr.has(Pins::PinAttributes::PullDown)) { + _output.value &= ~mask; + } + } else if (attr.has(Pins::PinAttributes::Output)) { + _configuration.value &= ~mask; + + if (attr.has(Pins::PinAttributes::InitialOn)) { + _output.value |= mask; + } + } + + if (attr.has(Pins::PinAttributes::ActiveLow)) { + _invert.value |= mask; + } + + // Ignore the ISR flag. ISR is fine. + + // Trigger an update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + _status |= 1; + } + + void IRAM_ATTR I2CExtender::writePin(pinnum_t index, bool high) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + uint64_t mask = 1ull << index; + if (high) { + _output.value |= mask; + } else { + _output.value &= ~mask; + } + + uint8_t dirtyMask = uint8_t(1 << (index / 8)); + _dirtyWriteBuffer |= dirtyMask; + + // Note that _status is *not* updated! flushWrites takes care of this! + } + + bool IRAM_ATTR I2CExtender::readPin(pinnum_t index) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + // There are two possibilities here: + // 1. We use an ISR, and that we can just use the information as-is as long as it's in sync. + // The ISR itself triggers the update. + // 2. We don't use an ISR and need to update from I2C before we can reliably use the value. + + if (!_interruptPin.defined()) { + _status |= 8; + } + while (_status != 0) { + vTaskDelay(1); + } + + // Use the value: + return ((_input.value >> index) & 1) == 1; + } + + void IRAM_ATTR I2CExtender::flushWrites() { + auto writeMask = _dirtyWriteBuffer.exchange(0); + + _dirtyWrite |= writeMask; + _status |= 4; + + while (_status != 0) { + vTaskDelay(1); + } + } + + void I2CExtender::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + Assert(mode == CHANGE, "Only mode CHANGE is allowed for pin extender ISR's."); + Assert(index < 64 && index >= 0, "Pin index out of range"); + + ISRData& data = _isrData[index]; + data.callback = callback; + data.data = arg; + + // Update continuous operation. + _operation &= ~8; + if (!_interruptPin.defined()) { + _operation |= 8 | 16; + } + + // Trigger task configuration update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // write fence first! + _status |= 1; + } + + void I2CExtender::detachInterrupt(pinnum_t index) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + ISRData& data = _isrData[index]; + data.callback = nullptr; + data.data = nullptr; + + // Check if we still need to ISR everything. Use a temporary to ensure thread safety: + auto newop = _operation; + newop &= ~8; + if (!_interruptPin.defined()) { + for (int i = 0; i < 64; ++i) { + if (_isrData[i].defined()) { + newop |= 8 | 16; + } + } + } + _operation = newop; + + // Trigger task configuration update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // write fence first! + _status |= 1; + } + + const char* I2CExtender::name() const { return "i2c_extender"; } + + I2CExtender::~I2CExtender() { + // The task might have allocated temporary data, so we cannot just destroy it: + _status |= 2; + + // Detach the interrupt pin: + if (_interruptPin.defined()) { + _interruptPin.detachInterrupt(); + } + + // Give enough time for the task to stop: + vTaskDelay(TaskDelayBetweenIterations * 10); + + // Should be safe now to stop. + _isrHandler = nullptr; + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("i2c_extender"); + } +} diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h new file mode 100644 index 000000000..b98ff6037 --- /dev/null +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -0,0 +1,176 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + enum class I2CExtenderDevice { + Unknown, + PCA9539, + }; + + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + // + // How this class works... + // + // A single device has a bunch of pins, normally 8 or 16. These devices also have an address. + // The maximum number of extender I/O pins that we currently support is currently 64. Note that + // the I2C frequency doesn't really matter if you have the ISR handled. We limit the maximum number + // of ports to 64 here, which basically means you *can* wire multiple devices on a single ISR line + // by making good use of the addresses. + // + // Keep in mind that low latency is only possible if you do things correctly with placement on the + // PCB, and with designating I/O ports for very specific purposes. If not, the firmware won't know + // what an interrupt means, and has to figure it out before it can take action. + // + // A typical configuration looks like: + // + // device: pca9539 + // device_id: 0 + // interrupt: gpio.36 + // + class I2CExtender : public PinExtenderDriver { + struct RegisterSet { + union { + uint64_t value = 0; + uint8_t bytes[8]; + }; + }; + + struct ISRData { + using ISRCallback = void (*)(void*); + ISRData() : callback(nullptr), data(nullptr) {} + + ISRCallback callback; + void* data; + + inline bool defined() const { return callback != nullptr; } + }; + + // Device info: + int _device = int(I2CExtenderDevice::Unknown); + int _deviceId = 0; + + static const int TaskDelayBetweenIterations = 10; + + // Operation and status work together and form a common bitmask. Operation is just not reset, + // while status is. + uint8_t _operation = 0; + + // This information is filled based on the "device" and "device_id" during initialization: + uint8_t _bus = 0; + uint8_t _address = 0x74; + uint8_t _ports = 16; + uint8_t _invertReg = 0xFF; + uint8_t _operationReg = 0xFF; + uint8_t _inputReg = 0xFF; + uint8_t _outputReg = 0xFF; + Pin _interruptPin; + + RegisterSet _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Current register values: + RegisterSet _configuration; + RegisterSet _invert; + volatile RegisterSet _input; + volatile RegisterSet _output; + + // I2C communications within an ISR is not a good idea, it will crash everything. We offload + // the communications using a task queue. Dirty tells which devices and registers to poll. + // Every I2C roundtrip is always responsible for 8 bytes. + TaskHandle_t _isrHandler = nullptr; + + uint8_t _usedIORegisters; + std::atomic _dirtyWriteBuffer; + std::atomic _dirtyWrite; + + // Status is a bitmask that tells the task handle what needs to happen during the next roundtrip. + // This works together with 'operation'. + std::atomic _status; + ISRData _isrData[64]; + + static void isrTaskLoop(void* arg); + void isrTaskLoopDetail(); + + public: + I2CExtender(); + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + + void group(Configuration::HandlerBase& handler) override; + + static void interruptHandler(void* arg); + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~I2CExtender(); + }; +} diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp deleted file mode 100644 index eea3cb056..000000000 --- a/FluidNC/src/Extenders/PCA9539.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) 2021 - Stefan de Bruijn -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. - -#include "Extenders.h" -#include "PCA9539.h" -#include "../Logging.h" - -#include -#include - -namespace Extenders { - void PCA9539::claim(pinnum_t index) { - Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); - - uint64_t mask = uint64_t(1) << index; - Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); - - _claimed |= mask; - } - - void PCA9539::free(pinnum_t index) { - uint64_t mask = uint64_t(1) << index; - _claimed &= ~mask; - } - - uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { - Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); - - auto err = bus->write(address, ®, 1); - - if (err) { - log_info("Error writing to i2c bus. Code: " << err); - return 0; - } - - uint8_t inputData; - if (bus->read(address, &inputData, 1) != 1) { - log_info("Error reading from i2c bus."); - } - - return inputData; - } - - void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { - Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); - - uint8_t data[2]; - data[0] = reg; - data[1] = uint8_t(value); - auto err = bus->write(address, data, 2); - - if (err) { - log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); - } - } - - void PCA9539::validate() const { - auto i2c = config->_i2c; - Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); - } - - void PCA9539::group(Configuration::HandlerBase& handler) { - handler.item("interrupt0", _isrData[0]._pin); - handler.item("interrupt1", _isrData[1]._pin); - handler.item("interrupt2", _isrData[2]._pin); - handler.item("interrupt3", _isrData[3]._pin); - } - - void PCA9539::isrTaskLoop(void* arg) { - auto inst = static_cast(arg); - while (true) { - void* ptr; - if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { - ISRData* valuePtr = static_cast(ptr); - log_info("PCA state change ISR"); - valuePtr->updateValueFromDevice(); - } - } - } - - void PCA9539::init() { - this->_i2cBus = config->_i2c; - - _isrQueue = xQueueCreate(16, sizeof(void*)); - xTaskCreatePinnedToCore(isrTaskLoop, // task - "isr_handler", // name for task - configMINIMAL_STACK_SIZE + 512, // size of task stack - this, // parameters - 1, // priority - &_isrHandler, - SUPPORT_TASK_CORE // core - ); - - for (int i = 0; i < 4; ++i) { - auto& data = _isrData[i]; - - data._address = uint8_t(0x74 + i); - data._container = this; - data._valueBase = reinterpret_cast(&_value) + i; - - // Update the value first by reading it: - data.updateValueFromDevice(); - - if (!data._pin.undefined()) { - data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); - - // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. - data._pin.attachInterrupt(updatePCAState, FALLING, &data); - } else { - // Reset valueBase so we know it's not bound to an ISR: - data._valueBase = nullptr; - } - } - } - - void PCA9539::ISRData::updateValueFromDevice() { - const uint8_t InputReg = 0; - auto i2cBus = _container->_i2cBus; - - auto r1 = I2CGetValue(i2cBus, _address, InputReg); - auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); - uint16_t oldValue = *_valueBase; - uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); - *_valueBase = value; - - if (_hasISR) { - for (int i = 0; i < 16; ++i) { - uint16_t mask = uint16_t(1) << i; - - if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { - log_info("State change pin " << i); - switch (_isrMode[i]) { - case RISING: - if ((value & mask) == mask) { - _isrCallback[i](_isrArgument); - } - break; - case FALLING: - if ((value & mask) == 0) { - _isrCallback[i](_isrArgument); - } - break; - case CHANGE: - _isrCallback[i](_isrArgument); - break; - } - } - } - } - } - - void PCA9539::updatePCAState(void* ptr) { - ISRData* valuePtr = static_cast(ptr); - - BaseType_t xHigherPriorityTaskWoken = false; - xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); - } - - void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { - bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); - bool output = attr.has(Pins::PinAttributes::Output); - - uint64_t mask = uint64_t(1) << index; - _invert = (_invert & ~mask) | (activeLow ? mask : 0); - _configuration = (_configuration & ~mask) | (output ? 0 : mask); - - const uint8_t deviceId = index / 16; - - const uint8_t ConfigReg = 6; - uint8_t address = 0x74 + deviceId; - - uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); - uint8_t reg = ConfigReg + ((index / 8) & 1); - - log_info("Setup reg " << int(reg) << " with value " << int(value)); - - I2CSetValue(_i2cBus, address, reg, value); - } - - void PCA9539::writePin(pinnum_t index, bool high) { - uint64_t mask = uint64_t(1) << index; - uint64_t oldVal = _value; - uint64_t newVal = high ? mask : uint64_t(0); - _value = (_value & ~mask) | newVal; - - _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); - } - - bool PCA9539::readPin(pinnum_t index) { - uint8_t reg = uint8_t(index / 8); - uint8_t deviceId = reg / 2; - - // If it's handled by the ISR, we don't need to read anything from the device. - // Otherwise, we do. Check: - if (_isrData[deviceId]._valueBase == nullptr) { - const uint8_t InputReg = 0; - uint8_t address = 0x74 + deviceId; - - auto readReg = InputReg + (reg & 1); - auto value = I2CGetValue(_i2cBus, address, readReg); - uint64_t newValue = uint64_t(value) << (int(reg) * 8); - uint64_t mask = uint64_t(0xff) << (int(reg) * 8); - - _value = ((newValue ^ _invert) & mask) | (_value & ~mask); - - log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); - } else { - log_info("No read, value is " << int(_value)); - } - - return (_value & (1ull << index)) != 0; - } - - void PCA9539::flushWrites() { - uint64_t write = _value ^ _invert; - for (int i = 0; i < 8; ++i) { - if ((_dirtyRegisters & (1 << i)) != 0) { - const uint8_t OutputReg = 2; - uint8_t address = 0x74 + (i / 2); - - uint8_t val = uint8_t(write >> (8 * i)); - uint8_t reg = OutputReg + (i & 1); - I2CSetValue(_i2cBus, address, reg, val); - } - } - - _dirtyRegisters = 0; - } - - // ISR's: - void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { - int device = index / 16; - int pinNumber = index % 16; - - Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); - - _isrData[device]._isrCallback[pinNumber] = callback; - _isrData[device]._isrArgument[pinNumber] = arg; - _isrData[device]._isrMode[pinNumber] = mode; - _isrData[device]._hasISR = true; - } - - void PCA9539::detachInterrupt(pinnum_t index) { - int device = index / 16; - int pinNumber = index % 16; - - _isrData[device]._isrCallback[pinNumber] = nullptr; - _isrData[device]._isrArgument[pinNumber] = nullptr; - _isrData[device]._isrMode[pinNumber] = 0; - - bool hasISR = false; - for (int i = 0; i < 16; ++i) { - hasISR |= (_isrData[device]._isrArgument[i] != nullptr); - } - _isrData[device]._hasISR = hasISR; - } - - const char* PCA9539::name() const { return "pca9539"; } - - PCA9539 ::~PCA9539() { - for (int i = 0; i < 4; ++i) { - auto& data = _isrData[i]; - - if (!data._pin.undefined()) { - data._pin.detachInterrupt(); - } - } - } - - // Register extender: - namespace { - PinExtenderFactory::InstanceBuilder registration("pca9539"); - } -} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h deleted file mode 100644 index d90068370..000000000 --- a/FluidNC/src/Extenders/PCA9539.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2021 - Stefan de Bruijn -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. - -#pragma once - -#include "PinExtenderDriver.h" -#include "../Configuration/Configurable.h" -#include "../Machine/MachineConfig.h" -#include "../Machine/I2CBus.h" -#include "../Platform.h" - -#include - -namespace Pins { - class PCA9539PinDetail; -} - -namespace Extenders { - // Pin extenders... - // - // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address - // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 - // separate registers, so that's a total of 16*4 = 64 values. - // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf - // Speed: 400 kHz - // - // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single - // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. - // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf - // Speed: 400 kHz - // - // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means - // that *some* pin has changed state. We don't know which one that was obviously. - // However, we can then query the individual pins (thereby resetting them) and throwing - // the results as individual ISR's. - // - // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the - // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless - // to say, this is usually a bad idea, because things like endstops become much slower - // as a result. For now, I just felt like not supporting it. - // - // The MCP23017 has two interrupt lines, one for register A and register B. Apart from - // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, - // so that's a total of 8 * 16 = 128 I/O ports. - // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf - // Speed: 100 kHz, 400 kHz, 1.7 MHz. - // - // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 - // seems to be the same, but 8-bit. - // - // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state - // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes - // pullups and schmitt triggers. - // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf - class PCA9539 : public PinExtenderDriver { - friend class Pins::PCA9539PinDetail; - - // Address can be set for up to 4 devices. Each device supports 16 pins. - - static const int numberPins = 16 * 4; - uint64_t _claimed; - - Machine::I2CBus* _i2cBus; - - static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); - static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); - - // Registers: - // 4x16 = 64 bits. Fits perfectly into an uint64. - uint64_t _configuration = 0; - uint64_t _invert = 0; - volatile uint64_t _value = 0; - - // 4 devices, 2 registers per device. 8 bits is enough: - uint8_t _dirtyRegisters = 0; - - QueueHandle_t _isrQueue = nullptr; - TaskHandle_t _isrHandler = nullptr; - - static void isrTaskLoop(void* arg); - - struct ISRData { - ISRData() = default; - - Pin _pin; - PCA9539* _container = nullptr; - volatile uint16_t* _valueBase = nullptr; - uint8_t _address = 0; - - typedef void (*ISRCallback)(void*); - - bool _hasISR = false; - ISRCallback _isrCallback[16] = { 0 }; - void* _isrArgument[16] = { 0 }; - int _isrMode[16] = { 0 }; - - void IRAM_ATTR updateValueFromDevice(); - }; - - ISRData _isrData[4]; - static void IRAM_ATTR updatePCAState(void* ptr); - - public: - PCA9539() = default; - - void claim(pinnum_t index) override; - void free(pinnum_t index) override; - - void validate() const override; - void group(Configuration::HandlerBase& handler) override; - - void init(); - - void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; - void IRAM_ATTR writePin(pinnum_t index, bool high) override; - bool IRAM_ATTR readPin(pinnum_t index) override; - void IRAM_ATTR flushWrites() override; - - void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; - void detachInterrupt(pinnum_t index) override; - - const char* name() const override; - - ~PCA9539(); - }; -} diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index 7158661de..dcae580d3 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -4,6 +4,7 @@ #include "I2CBus.h" #include +#include namespace Machine { void I2CBus::validate() const { @@ -25,30 +26,56 @@ namespace Machine { auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + Assert(_busNumber == 0 || _busNumber == 1, "Bus # has to be 0 or 1; the ESP32 does not have more i2c peripherals."); + if (_busNumber == 0) { i2c = &Wire; } else { i2c = &Wire1; } - i2c->begin(sdaPin, sclPin /*, _frequency */); + i2c->begin(int(sdaPin), int(sclPin), _frequency); - log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); + log_info("I2C SDA: " << _sda.name() << ", SCL: " << _scl.name() << ", Freq: " << _frequency << ", Bus #: " << _busNumber); } + const char* I2CBus::ErrorDescription(int code) { + switch (code) { + case I2C_ERROR_OK: + return "ok"; + case I2C_ERROR_DEV: + return "general device error"; + case I2C_ERROR_ACK: + return "no ack returned by device"; + case I2C_ERROR_TIMEOUT: + return "timeout"; + case I2C_ERROR_BUS: + return "bus error"; + case I2C_ERROR_BUSY: + return "device busy"; + case I2C_ERROR_MEMORY: + return "insufficient memory"; + case I2C_ERROR_CONTINUE: + return "continue"; + case I2C_ERROR_NO_BEGIN: + return "begin transmission missing"; + default: + return "unknown"; + } + } int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { - // log_info("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " - // << (i2c ? "non null" : "null")); + // log_debug("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); i2c->beginTransmission(address); for (size_t i = 0; i < count; ++i) { i2c->write(data[i]); } - return i2c->endTransmission(); // i2c_err_t ?? + return i2c->endTransmission(); // i2c_err_t, see header file } int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { - // log_info("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " - // << (i2c ? "non null" : "null")); + // log_debug("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); for (size_t i = 0; i < count; ++i) { if (i2c->requestFrom((int)address, 1) != 1) { diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index febbf51fc..f7ddab35e 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -20,27 +20,15 @@ namespace Machine { int _busNumber = 0; Pin _sda; Pin _scl; - uint32_t _frequency = 0; + uint32_t _frequency = 100000; void init(); void validate() const override; void group(Configuration::HandlerBase& handler) override; - /* - typedef enum { - I2C_ERROR_OK=0, - I2C_ERROR_DEV, - I2C_ERROR_ACK, - I2C_ERROR_TIMEOUT, - I2C_ERROR_BUS, - I2C_ERROR_BUSY, - I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE, - I2C_ERROR_NO_BEGIN - } i2c_err_t; - */ - - int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! + static const char* ErrorDescription(int code); + + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); ~I2CBus() = default; diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 93d739c8d..5f2cffa0b 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -34,7 +34,7 @@ - + @@ -182,6 +182,7 @@ + @@ -218,7 +219,7 @@ - + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index d8f5ac631..72c058c0e 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -582,9 +582,6 @@ src\Machine - - src\Extenders - src\Extenders @@ -597,6 +594,12 @@ src\Pins + + X86TestSupport + + + src\Extenders + @@ -1016,9 +1019,6 @@ src\Machine - - src\Extenders - src\Extenders @@ -1031,6 +1031,9 @@ src\Pins + + src\Extenders + diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index aa9f01dcf..98cddae8a 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -1,6 +1,7 @@ #pragma once #include +#include "esp32-hal-i2c.h" #include "Stream.h" class TwoWire : public Stream { diff --git a/X86TestSupport/TestSupport/esp32-hal-i2c.h b/X86TestSupport/TestSupport/esp32-hal-i2c.h new file mode 100644 index 000000000..066108aad --- /dev/null +++ b/X86TestSupport/TestSupport/esp32-hal-i2c.h @@ -0,0 +1,13 @@ +#pragma once + +typedef enum { + I2C_ERROR_OK = 0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; From 2e5f4018f4c2cb886c24916581a3b54c038d510e Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Wed, 2 Mar 2022 15:07:40 +0100 Subject: [PATCH 071/100] Fixed a few small bugs. It's not working yet, but getting there... --- FluidNC/src/Extenders/I2CExtender.cpp | 5 ++++- FluidNC/src/Extenders/I2CExtender.h | 2 -- FluidNC/src/Machine/I2CBus.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 8a824a24f..4c5b32e69 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -13,6 +13,8 @@ #include namespace Extenders { + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { @@ -216,7 +218,7 @@ namespace Extenders { // device: pca9539 // device_id: 0 // interrupt: gpio.36 - handler.item("device", _device); + handler.item("device", _device, i2cDevice); handler.item("device_id", _deviceId); handler.item("interrupt", _interruptPin); } @@ -228,6 +230,7 @@ namespace Extenders { void I2CExtender::init() { Assert(_isrHandler == nullptr, "Init has already been called on I2C extender."); + this->_i2cBus = config->_i2c; switch (I2CExtenderDevice(_device)) { case I2CExtenderDevice::PCA9539: diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index b98ff6037..c94e25f99 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -21,8 +21,6 @@ namespace Extenders { PCA9539, }; - EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; - // Pin extenders... // // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index dcae580d3..daf5120a2 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -18,7 +18,7 @@ namespace Machine { void I2CBus::group(Configuration::HandlerBase& handler) { handler.item("sda", _sda); handler.item("scl", _scl); - handler.item("busNumber", _busNumber); + handler.item("bus", _busNumber); handler.item("frequency", _frequency); } From 18ff4f05eb91e8c2c7554c4a35a2ee93d735c075 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 12:23:26 +0100 Subject: [PATCH 072/100] Added first I2C extender unit tests --- FluidNC/src/Extenders/I2CExtender.cpp | 51 +-- FluidNC/src/Extenders/I2CExtender.h | 9 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 340 +++++++++++++++++++ UnitTests.vcxproj | 1 + UnitTests.vcxproj.filters | 6 + X86TestSupport/TestSupport/Capture.h | 2 +- X86TestSupport/TestSupport/Wire.cpp | 146 ++++++++ X86TestSupport/TestSupport/Wire.h | 92 ++--- X86TestSupport/TestSupport/freertos/Task.cpp | 1 + 9 files changed, 582 insertions(+), 66 deletions(-) create mode 100644 FluidNC/test/Extender/I2CExtenderTests.cpp diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 4c5b32e69..b7dea07c7 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -15,7 +15,7 @@ namespace Extenders { EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; - I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} + I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { int err; @@ -67,6 +67,7 @@ namespace Extenders { if (newStatus != 0) { if ((newStatus & 2) != 0) { + _status = 0; break; } @@ -81,34 +82,34 @@ namespace Extenders { claimedValues = 0; for (int i = 0; i < 8; ++i) { if (_claimed.bytes[i] != 0) { - claimedValues = i; + claimedValues = i + 1; } } - // Configuration: - { + // Invert: + if (_invertReg != 0xFF) { + uint8_t currentRegister = _invertReg; + uint8_t address = _address; for (int i = 0; i < claimedValues; ++i) { - uint8_t currentRegister = _operationReg; - uint8_t address = _address; - - uint8_t by = _configuration.bytes[i]; + uint8_t by = _invert.bytes[i]; I2CSetValue(this->_i2cBus, address, currentRegister, by); currentRegister++; - if (currentRegister == registersPerDevice + _operationReg) { + if (currentRegister == registersPerDevice + _invertReg) { ++address; } } } - // Invert: - if (_invertReg != 0xFF) { - uint8_t currentRegister = _invertReg; - uint8_t address = _address; + // Configuration: + { for (int i = 0; i < claimedValues; ++i) { - uint8_t by = _invert.bytes[i]; + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + + uint8_t by = _configuration.bytes[i]; I2CSetValue(this->_i2cBus, address, currentRegister, by); currentRegister++; - if (currentRegister == registersPerDevice + _invertReg) { + if (currentRegister == registersPerDevice + _operationReg) { ++address; } } @@ -116,7 +117,8 @@ namespace Extenders { // Configuration changed. Writes and reads must be updated. if (_outputReg != 0xFF) { - newStatus |= 4; // writes + newStatus |= 4; // writes + _dirtyWrite = 0xFF; // everything is dirty. } if (_inputReg != 0xFF) { newStatus |= 8; // reads @@ -248,6 +250,9 @@ namespace Extenders { break; } + // Ensure data is available: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + xTaskCreatePinnedToCore(isrTaskLoop, // task "i2cHandler", // name for task configMINIMAL_STACK_SIZE + 512, // size of task stack @@ -300,15 +305,19 @@ namespace Extenders { void IRAM_ATTR I2CExtender::writePin(pinnum_t index, bool high) { Assert(index < 64 && index >= 0, "Pin index out of range"); - uint64_t mask = 1ull << index; + uint64_t mask = 1ull << index; + auto oldValue = _output.value; if (high) { _output.value |= mask; } else { _output.value &= ~mask; } - uint8_t dirtyMask = uint8_t(1 << (index / 8)); - _dirtyWriteBuffer |= dirtyMask; + // Did something change? + if (oldValue != _output.value) { + uint8_t dirtyMask = uint8_t(1 << (index / 8)); + _dirtyWriteBuffer |= dirtyMask; + } // Note that _status is *not* updated! flushWrites takes care of this! } @@ -398,7 +407,9 @@ namespace Extenders { } // Give enough time for the task to stop: - vTaskDelay(TaskDelayBetweenIterations * 10); + for (int i = 0; i < 10 && _status != 0; ++i) { + vTaskDelay(TaskDelayBetweenIterations); + } // Should be safe now to stop. _isrHandler = nullptr; diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index c94e25f99..222226d53 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -144,18 +144,16 @@ namespace Extenders { static void isrTaskLoop(void* arg); void isrTaskLoopDetail(); + static void interruptHandler(void* arg); + public: I2CExtender(); void claim(pinnum_t index) override; void free(pinnum_t index) override; - void validate() const override; - void group(Configuration::HandlerBase& handler) override; - - static void interruptHandler(void* arg); - + void validate() const override; void init(); void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; @@ -164,7 +162,6 @@ namespace Extenders { void IRAM_ATTR flushWrites() override; void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; - void detachInterrupt(pinnum_t index) override; const char* name() const override; diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp new file mode 100644 index 000000000..2db277858 --- /dev/null +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -0,0 +1,340 @@ +#include "../TestFramework.h" + +#include +#include +#include +#include +#include + +#include "Capture.h" + +namespace Configuration { + Test(I2CExtender, I2CBasics) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + + bus.validate(); + bus.init(); + + Wire.Clear(); + + Assert(0 == bus.write(1, reinterpret_cast("aap"), 3), "Bad write"); + auto data = Wire.Receive(); + + Assert(data.size() == 3, "Expected 3 bytes"); + data.push_back(0); + Assert(!strcmp(reinterpret_cast(data.data()), "aap"), "Incorrect data read"); + + uint8_t tmp[4]; + tmp[3] = 0; + Assert(bus.read(1, tmp, 3) == 0, "Expected no data available for read"); + + std::vector tmp2; + tmp2.push_back(uint8_t('p')); + tmp2.push_back(uint8_t('i')); + tmp2.push_back(uint8_t('m')); + + Wire.Send(tmp2); + Assert(bus.read(1, tmp, 3) == 3, "Expected 3 bytes data available for read"); + Assert(bus.read(1, tmp, 3) == 0, "Expected no data available for read"); + Assert(!strcmp(reinterpret_cast(tmp), "pim"), "Incorrect data read"); + } + + // Helper class for initialization of I2C extender + class FakeInitHandler : public Configuration::HandlerBase { + bool hasISR_; + + protected: + void enterSection(const char* name, Configurable* value) override {} + bool matchesUninitialized(const char* name) override { return true; } + + public: + FakeInitHandler(bool hasISR) : hasISR_(hasISR) {} + + void item(const char* name, float& value, float minValue = -3e38, float maxValue = 3e38) override {} + void item(const char* name, std::vector& value) override {} + void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override {} + void item(const char* name, Pin& value) override { + if (!strcmp(name, "interrupt") && hasISR_) { + value = Pin::create("gpio.15"); + } + } + void item(const char* name, IPAddress& value) override {} + void item(const char* name, int& value, EnumItem* e) override { + if (!strcmp(name, "device")) { + value = int(Extenders::I2CExtenderDevice::PCA9539); + } + } + + void item(const char* name, String& value, int minLength = 0, int maxLength = 255) override {} + + HandlerType handlerType() override { return HandlerType::Parser; } + + void item(const char* name, bool& value) override {} + void item(const char* name, int32_t& value, int32_t minValue = 0, int32_t maxValue = INT32_MAX) override { + if (!strcmp(name, "device_id")) { + value = 0; + } + } + }; + + Test(I2CExtender, InitDeinit) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + } + + Test(I2CExtender, ClaimRelease) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + i2c.claim(1); + i2c.claim(0); + AssertThrow(i2c.claim(1)); + i2c.claim(2); + AssertThrow(i2c.claim(64)); + AssertThrow(i2c.claim(-1)); + i2c.free(1); + i2c.free(1); + i2c.claim(1); + AssertThrow(i2c.claim(1)); + i2c.free(0); + i2c.free(1); + i2c.free(2); + } + + class Roundtrip { + uint32_t before; + + public: + Roundtrip() { before = Capture::instance().current(); } + + ~Roundtrip() { + while (Capture::instance().current() < before + 1) { + delay(10); + } + } + }; + + Test(I2CExtender, SetupPin) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + // Expected register values (see datasheet): + // + // 4 invert + // 1 = invert, 0 = normal + // + // 6 config + // 1 = input, 0 = output + // + // 2 write + // 1 = high, 0 = low + // + // 0 read + // high = 1, low = 0 + + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x01); + + i2c.claim(0); + i2c.setupPin(0, Pin::Attr::Output); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), + uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Read will trigger an update, because we don't have an ISR + { + Wire.Send(0x01); + bool readPin = i2c.readPin(0); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test write pin: + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + } + { + // Write to set it 'low'. + i2c.writePin(0, false); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 0, "Expected write reg 0 = 0"); + } + { + // Write to set it 'low'. It's already low, no-op. + i2c.writePin(0, false); + i2c.flushWrites(); + // no-op. + } + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + } + + // NOTE: We ended with setting pin #0 to 'high' = 0x01 + + // Setup pin for reading: + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + + i2c.claim(1); + i2c.setupPin(1, Pin::Attr::Input); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), + uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Setup another pin for reading with an invert mask and a PU: + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x04); + + i2c.claim(2); + i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), + uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Test read pin: + { + Wire.Send(0x02); + bool readPin = i2c.readPin(1); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test read pin: + { + Wire.Send(0x02); + bool readPin = i2c.readPin(2); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == false, "Expected 'true' on pin"); + } + } +} diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 5f2cffa0b..f74585854 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -279,6 +279,7 @@ + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 72c058c0e..716078b48 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -49,6 +49,9 @@ {2b9b9202-4bb3-450e-a90b-99f042de0af2} + + {4f2af482-2e54-41bf-b698-1ab77a1a5ea3} + @@ -1034,6 +1037,9 @@ src\Extenders + + test\Extender + diff --git a/X86TestSupport/TestSupport/Capture.h b/X86TestSupport/TestSupport/Capture.h index f8d3be1df..e9ce30a7c 100644 --- a/X86TestSupport/TestSupport/Capture.h +++ b/X86TestSupport/TestSupport/Capture.h @@ -21,7 +21,7 @@ class Capture { Capture() = default; std::vector events; - uint32_t currentTime = 0; + volatile uint32_t currentTime = 0; public: static Capture& instance() { diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 046c93eda..69373b035 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -2,3 +2,149 @@ TwoWire Wire(0); TwoWire Wire1(1); + +TwoWire::TwoWire(uint8_t bus_num) {} +TwoWire::~TwoWire() {} + +// For unit tests: +void TwoWire::Send(std::vector data) { + for (auto it : data) { + Send(it); + } +} +void TwoWire::Send(uint8_t value) { + receivedData.push_back(value); +} +std::vector TwoWire::Receive() { + std::vector data; + std::swap(sentData, data); + return data; +} + +void TwoWire::Clear() { + sentData.clear(); + receivedData.clear(); +} + +// TwoWire interface: + +// call setPins() first, so that begin() can be called without arguments from libraries +bool TwoWire::setPins(int sda, int scl) { + return true; +} + +bool TwoWire::begin(int sda, int scl, uint32_t frequency) { + return true; +} // returns true, if successful init of i2c bus + +bool TwoWire::begin(uint8_t slaveAddr, int sda, int scl, uint32_t frequency) { + return true; +} +bool TwoWire::end() { + return true; +} + +void TwoWire::setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms +uint16_t TwoWire::getTimeOut() { + return 0; +} + +bool TwoWire::setClock(uint32_t) { + return true; +} +uint32_t TwoWire::getClock() { + return 0; +} + +void TwoWire::beginTransmission(uint16_t address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} +void TwoWire::beginTransmission(uint8_t address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} +void TwoWire::beginTransmission(int address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} + +uint8_t TwoWire::endTransmission(bool sendStop) { + Assert(inTransmission, "Should be in a transmission"); + inTransmission = false; + return 0; +} +uint8_t TwoWire::endTransmission(void) { + Assert(inTransmission, "Should be in a transmission"); + inTransmission = false; + return 0; +} + +size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { + auto available = receivedData.size(); + if (available > size) { + available = size; + } + return available; +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop)); +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop != 0)); +} +size_t TwoWire::requestFrom(uint8_t address, size_t len, bool stopBit) { + return uint8_t(requestFrom(uint16_t(address), size_t(len), stopBit)); +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size) { + return uint8_t(requestFrom(address, size_t(size), false)); +} +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop != 0)); +} +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t size) { + return uint8_t(requestFrom(address, size_t(size), false)); +} +uint8_t TwoWire::requestFrom(int address, int size, int sendStop) { + return uint8_t(requestFrom(uint16_t(address), size_t(size), sendStop != 0)); +} +uint8_t TwoWire::requestFrom(int address, int size) { + return uint8_t(requestFrom(uint16_t(address), size_t(size), false)); +} + +size_t TwoWire::write(uint8_t ch) { + Assert(inTransmission, "Should be in a transmission"); + sentData.push_back(ch); + return 0; +} + +size_t TwoWire::write(const uint8_t* buf, size_t size) { + for (size_t i = 0; i < size; ++i) { + write(buf[i]); + } + return size; +} +int TwoWire::available(void) { + return receivedData.size(); +} +int TwoWire::read(void) { + if (receivedData.size()) { + auto result = receivedData[0]; + receivedData.erase(receivedData.begin()); + return result; + } else { + return -1; + } +} +int TwoWire::peek(void) { + if (receivedData.size()) { + auto result = receivedData[0]; + return result; + } else { + return -1; + } +} +void TwoWire::flush(void) {} + +extern TwoWire Wire; +extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index 98cddae8a..1bda1ea24 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -1,50 +1,64 @@ #pragma once #include +#include #include "esp32-hal-i2c.h" #include "Stream.h" +#include "../FluidNC/test/TestFramework.h" class TwoWire : public Stream { + bool inTransmission = false; + std::vector receivedData; + std::vector sentData; + public: - TwoWire(uint8_t bus_num) {} - ~TwoWire() {} - - //call setPins() first, so that begin() can be called without arguments from libraries - bool setPins(int sda, int scl) {} - - bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } // returns true, if successful init of i2c bus - bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } - bool end() { return true; } - - void setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms - uint16_t getTimeOut() { return 0; } - - bool setClock(uint32_t) {} - uint32_t getClock() { return 0; } - - void beginTransmission(uint16_t address) {} - void beginTransmission(uint8_t address) {} - void beginTransmission(int address) {} - - uint8_t endTransmission(bool sendStop) { return 0; } - uint8_t endTransmission(void) { return 0; } - - size_t requestFrom(uint16_t address, size_t size, bool sendStop) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { return 0; } - size_t requestFrom(uint8_t address, size_t len, bool stopBit) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t size) { return 0; } - uint8_t requestFrom(int address, int size, int sendStop) { return 0; } - uint8_t requestFrom(int address, int size) { return 0; } - - size_t write(uint8_t) { return 0; } - size_t write(const uint8_t*, size_t) { return 0; } - int available(void) { return 0; } - int read(void) { return 0; } - int peek(void) { return 0; } - void flush(void) {} + TwoWire(uint8_t bus_num); + ~TwoWire(); + + // For unit tests: + void Send(std::vector data); + void Send(uint8_t value); + std::vector Receive(); + void Clear(); + + // TwoWire interface: + + // call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl); + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0); // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0); + bool end(); + + void setTimeOut(uint16_t timeOutMillis); // default timeout of i2c transactions is 50ms + uint16_t getTimeOut(); + + bool setClock(uint32_t); + uint32_t getClock(); + + void beginTransmission(uint16_t address); + void beginTransmission(uint8_t address); + void beginTransmission(int address); + + uint8_t endTransmission(bool sendStop); + uint8_t endTransmission(void); + + size_t requestFrom(uint16_t address, size_t size, bool sendStop); + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); + uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop); + size_t requestFrom(uint8_t address, size_t len, bool stopBit); + uint8_t requestFrom(uint16_t address, uint8_t size); + uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop); + uint8_t requestFrom(uint8_t address, uint8_t size); + uint8_t requestFrom(int address, int size, int sendStop); + uint8_t requestFrom(int address, int size); + + size_t write(uint8_t ch); + size_t write(const uint8_t* buf, size_t size); + int available(void); + int read(void); + int peek(void); + void flush(void); inline size_t write(const char* s) { return write((uint8_t*)s, strlen(s)); } inline size_t write(unsigned long n) { return write((uint8_t)n); } diff --git a/X86TestSupport/TestSupport/freertos/Task.cpp b/X86TestSupport/TestSupport/freertos/Task.cpp index 8dd60b5c6..05036f34c 100644 --- a/X86TestSupport/TestSupport/freertos/Task.cpp +++ b/X86TestSupport/TestSupport/freertos/Task.cpp @@ -27,6 +27,7 @@ BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode, void vTaskDelay(const TickType_t xTicksToDelay) { Capture::instance().wait(xTicksToDelay); + delay(xTicksToDelay); } void vTaskDelayUntil(TickType_t* const pxPreviousWakeTime, const TickType_t xTimeIncrement) { From d932cd4f1a838773d78f32c195032bbe942cd5ec Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 14:46:09 +0100 Subject: [PATCH 073/100] Fixed a few bugs. According to the tests this should pretty much work. --- CodeCoverage.cov | 672 +++++++++++++++++++++ FluidNC/src/Extenders/I2CExtender.cpp | 71 ++- FluidNC/src/Extenders/I2CExtender.h | 8 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 474 ++++++++++++++- FluidNC/test/Pins/GPIO.cpp | 55 +- X86TestSupport/TestSupport/SoftwareGPIO.h | 2 +- X86TestSupport/TestSupport/Wire.cpp | 29 +- X86TestSupport/TestSupport/Wire.h | 9 + 8 files changed, 1251 insertions(+), 69 deletions(-) create mode 100644 CodeCoverage.cov diff --git a/CodeCoverage.cov b/CodeCoverage.cov new file mode 100644 index 000000000..a3f4cc636 --- /dev/null +++ b/CodeCoverage.cov @@ -0,0 +1,672 @@ +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.cpp +RES: _______cccccc_uuuuuu_uuuuuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_ccccc_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___ccccccccccccccc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\LegacySettingRegistry.h +RES: ____________uuuu______________u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\FS.h +RES: ________________________________________u________u_____________________c___________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10vSpindle.cpp +RES: ______________________uu__uuu__u_uuuu_uu_____u_u_u_uu__uu__u___uuuu_uuu__u__uuuu_u_uuuuu_uuuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StandardStepper.cpp +RES: _______________________u_uuuu_uu_uuuu_u_uu_uu_u_uuuuuuuuuuu_uuuu_uuu__uuuuuuuuuuu__uu_uuu_uuuuuu_u_uuu_u_u_u___c__uuuuuuu__uuuu___u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.h +RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________ccccuuuu__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Authentication.cpp +RES: ___________________________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.cpp +RES: ____cc_c__ccccccccccccccc_cccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.cpp +RES: _______uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPI.h +RES: ____uu___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\InputBuffer.h +RES: ____________u__________u_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SDFS.cpp +RES: ____cuuuuuuuuu_c__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.h +RES: ___________c______________c_c_c_________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.cpp +RES: ______________________________u_u_uuu_uuu__u_u_uuu_uuu___u_uuuu_uu_uu_uuu_____uuuuuu__uuu___uuuuuuuu_uuu_________________________________________________________________________________________uuu___uuu__u_uu___u_uuuu_uuu_uu_uu__uuu___uuuu_u_u_uu___u_uuuuuuuu__uuu_uu_uuuu_u______uuuuuu_uuuuu_____uuu_uuuuu_uuuuu_uuuuuuuuu_uuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPIFFS.cpp +RES: ____cu___uuuuuuu__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.cpp +RES: ____________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.cpp +RES: ________c_u____________cccuucc___u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.cpp +RES: ______________________________________ccccccc_u_u__uuuuuuu_uuuuu_u_uuuu_uu_uuu_ccc_uuu_uuu_ccc_ccc_cccuu_cc_ccuuc_c_uuuuu_uu_uuuuu_u_uuu_uuu_uuuu__uuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___c_c_c__cu___cc_cc_cc_u_u_u__uu___uuu_uu_uu_uu_uu_uu_uu_uu___uuu___uuuu_u__uuu__uu___uuuuuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.h +RES: ___________________________________________________________________________________c______c______c________u__cccc__uu_cc___cccc____c_c_u_uu_c____c_c__u_c___u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\rmt.cpp +RES: ____uuu_u_uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.h +RES: __________________________________________cc____ccu_cc_u______u_______________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\SPIBus.cpp +RES: __________uuuuu_u_uuu_uuu____uuuuu_u_uuuuu__u_________u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\nvs.cpp +RES: __uuuuuuuuu_uuuuuu_uuuuuu_uuuuuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\BTConfig.h +RES: __________uuu_u__________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\nvs.h +RES: ____________uuuuuuu_u_uuuuuuu_u_uuuuuuu_uuuu_uuu_u_uuuuuuu_uuuu_uuu_u_u_uuuu_u_u_uuuu__________uuuuuuu______________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Report.cpp +RES: ________________________________________________uuu_u_u__uuuuuuuu__uuuuu_u________uuu_uu____uuuuuuu__uuu_uu________uu____u_uu_u_u_uu_uuu__u_u_u_c______________c______uuuu_u___uu__________u__uu____u___uuuuu__u_uuuuu_uuu_uuuu_uuu_u__uuu_u_uu_uu_uu_uu_uu_uu_uu_uu_u__u_u_u_uu_uu_u__u_u_uu_u__u_u_uu_u__u_________u_uu_u__u__u_uu_uu_uu_uu_u__u_u_uu_uu_uu_u_u__uuuu_uu_uu___uu__uuuuuu__uuuuu_uuu______uuu_uu_uu___u_u_uuu_uuu_u___uuu______uuuuu_u_uu_uuu_uuu_uuuuuuu_uu_u_u_uu__u_u__u_u_uu_uu__u_u_uu_uuuuuu_u__uuuuuuuu_u_u__uuuuu_u_u______uu__uuuuuu_u___uu__u_uuuuu_____uuu_u_u_uuuu_____u_u__uu_uu__uuuu_____u_u___uuuuuu_u_uu_u_____u_uu_uu___uu_______uu_u__uuuuu_uu_u_______u_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\I2SO.cpp +RES: ____uuu_u_u_uuu_uuu_u_uuu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\FS.cpp +RES: _________________________uuu__uu_uuu__uu_uuu__uu_uuu__uu_uuu___uu__uu_uuu__uu_uuu__uuuuu_uuu__uu_uuu__uu_uuu__uu_uuu__uu_uuuu_u_u_uuu__uu_uuu__uu__uuu_uu_uuu_uu_uuu_uu_u_uuu__uu_uuu_uu_u_uuu_uu_u_uuu_uu_u_uuu_uu_u_uuu_uu_u_u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\LegacySettingRegistry.cpp +RES: ________u_u_uuu_uu_uuuu_uu_uuu_uuu_u_uuuuuu__u_uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Task.cpp +RES: _____________c_______ccccc_cccp_uuu_uuuuu_uuu_uuu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Capture.h +RES: _______________________c__cccc___uuuuuuuu__________________________ccuuu_u_c_____uuuu_____________uuuuuu_u_uu_uuuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spindle.h +RES: ______________________________uu____________uu__u___u__uu___uu_u_______u_u___uuuu_uu_u__u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Queue.cpp +RES: ______uuuuuuu_uu_uu_uuu_u_uuu_u____uu_uuu_uu_uuuu_u_uu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\ExceptionHelper.cpp +RES: _______________________________________cccc__cc__ccu__cccc_______c_cc____ccc_ccccccccccccccc__ccccccc_c__cu__cccccccc__ccc_c_________ccccccc_cccc_c__cccc_ccuc_uc_c__ccccuc_cc__cu__cu_c_u_uc___uuuuuu_u__u___u______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\esp32-hal-timer.cpp +RES: _________uuuu_uuu_uuu_uu___uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\LimitPin.h +RES: _____________u_____uu___u_________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.cpp +RES: __________c_ccu_uuuu_u_uuu___uu_u__uuu_uuuuu_u_uuu_u_c_c_u_u_uu_u_uu_uuuu_c_cuucuuuu_ccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\GPIO.cpp +RES: _____________________________uu__uu__u_u__u_________pu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu_uu_uu_uu_uuuuuuu______________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu_p_p_p_pu_uu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uu_uuuuu_u_uuuu_u_uuuuu_pu_uuu_pu_uuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu______uu_uu_uu_uu_____________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu__p_p_p_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\LedcPin.cpp +RES: ______________________u________uuuu_uu_u_u_u____uuuuuu_uuu____uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StandardStepper.h +RES: _________________u__________________uuuuu__u_____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10vSpindle.h +RES: ___________________________________u_uuuuu__u_u_________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-internal.h +RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________p____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StepStick.cpp +RES: __________u_uuu_uu__u_uu_uuuuu_uuu__uu_u__u___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Regex.cpp +RES: _____________________u_uu_uuu__uuu_uu_uu_uu_uu___uuu__uu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\IPAddress.h +RES: ____________________uccccccu_u_uu___u_________________u__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\GenericFactory.h +RES: ____________cccc__________c_____u______c_____c_u__uuuuuu_u_uuu_uuuuuuuu_u_uuuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\xmodem.cpp +RES: ________________________________________u_uuuuuu______________________u_uuuuu_____________uuuuuuu_uuuuuu__uu_uuuu______________uu_u_uuu_uuu_uuuuuu_uuuuuuuu_u___uuuuu_u__uuuuu_uu_uu_uuuuu_uuuuu______uuuu_uuuuuu__uuuuuuuuuu_uuuuu_uuuuuuu_uu__uuuuu_uu__uuuu___uuu_uu_uu_uuuu______uuuuuu____uu____uu_uuuuuuuuuuuuuuuu_uuuuuu_uuu_uuuu_______uuuuuuuuuuuuuu_uuu_______________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Channel.h +RES: ______________________________c__c__u__u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.h +RES: ____________________________cccc________________uuu______u__uuuuu______uuu_u_u_uuuuu_uuu_______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spindle.cpp +RES: ________________u_uuuu_uuuuu_u_uuuuu_u__uu_uuu_uuuu_________uuu_uuuu__uuu__uuuuu_uuuu_u_uuuuu_uuuuuuu_uu_uuu_uuuu_uu_u_uuu_uu_______uu___uuuuu___u_u_uu_u__u___u_u__uu_uuuu_u_uu___u_u__uu_uuu_uuuu____uu_uu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.cpp +RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__cuuuuuu_uuuuuuuuu_uu_uc__cc_u_c_uuu_uuuuuuuu_c_____c__c___c__c_ccuu___cucu___cu__cc_cccc_cccc_cccu_cu_cu__cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.cpp +RES: ____________uu_uu__u__uu_u_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\RcServo.h +RES: ________________u_____uu_uu_u_u______________uuuuuu__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\DebugPinDetail.cpp +RES: ___________u___uuuuuuuuu__uuuuu__uu_uuuu_uuu_uuu_uu_uu_uu_uu_uu_uu_u_uu_uu_u_uuuu_uu__uuuu_uu_uuu_u_u_uuuuuuuuuuuuuuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\VFDSpindle.cpp +RES: _________________________________________u____uu________________u__u___uu__u_uuu__uuu_u__u_uu_u___u_uu_uuu_u__u_____uuuu_uuuu___uuuu____uu_________uu_______u__uuuu_________uu_uuu__uuu___uu__u_uuu__u___u___uu__uu_uu_u_u__uuu__uu___u______u_uuuuu_uuuu__uu___uuu___uu__u_uuu_____u_u__uuu_________u_uu____u_u_uuuu__u_uu_u_u_uu_u_u__uuu____u_uu_uuu_u__________u__uuuu_____uuuu__u_uu__u_u_u_uuu___uuu_uuu_uuuuu__u_uuu__u_u_uuuuu__u_uu_uuuuu__u_uuuu_u_____u___u_uu_____uuuu_uuuuuu_uu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\MotorDriver.cpp +RES: ____________________________uuu_u_u_uuuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.h +RES: ______________________________________u____u__uu__uc____________cc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.cpp +RES: _____________u_uuu_uu_uuuuu____uu__u_uu_uuuu_u_u_____________uu_u_uu__uuuu_uuuuu_uuu_u_uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.h +RES: ____________________u_______uuuuuuuuu_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\RelaySpindle.cpp +RES: _______________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SDCard.h +RES: ________________________________________________________________________uuuu___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\NullSpindle.cpp +RES: _________________uuuuuuuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\uartdriver.cpp +RES: ________uuu_uu_uuuuuu_uuu_uuuuuu_uuuuuuuuuuu_uuuuuuuuuuuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\RelaySpindle.h +RES: ______________________u____u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SDCard.cpp +RES: _____________u_______uu_u_u__u_u___uuu___uu__uu___u_u_u__uuu__uu_uuu_uuu_uu_uuu_uuuu_u__uuuuuuu_u___uuu_u___u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WifiServices.cpp +RES: _________c_cu_uuu____________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\YL620Spindle.cpp +RES: ____________________________________________________________________________u_uuu__uuu_uu_uu_uu_u__u_u____uu_uuuuuu_uuuu_uuuuu___uuu_____uuuuu_uuuuu___uuu____u_uu_u__uu________uuuu_u_uuu__uuuuu___uu_u_uuuu_uuu__uuuuu_____uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\YL620Spindle.h +RES: __________uu_______u_uu__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\CoolantControl.cpp +RES: ______u__uuuu__uu_uu__uu_uu_uu___uu_uu___uu_uuuu__uuu_u___uuuu______uuu_uuu_uuuu_uuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\FreeRTOS.h +RES: ____________________________uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Serial.cpp +RES: ________________________________________________________________u_uuuuuu_u_____uu__uu_uuu_uu_uu_uu_uu_uu_u____u_uu_uu_uuu_u_uuu_u_uuu_u_uuu_u_uu_uu_uu_uu_uu_uuu_u_uuu_u_uuu_u_uuu_u_uu_u__________u__uuu_uu____u_uuuuu_uuuuuu_uuuuuuuu_uuuuuuuuuuuuu____u_uuu_u_uu_uuu_c_u_____uu_uuu_u_u_uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Serial.h +RES: ________________________________________________________________________c_________uuuu___u_______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ControlPin.h +RES: _____________u___uuu____u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Control.cpp +RES: ________uuu_uuuuuuuuuu_uuuuuuuuuu_uu_u_uu_u__u___uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\dac.cpp +RES: ______uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.h +RES: _________________________u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\LimitPin.cpp +RES: _____________uu_u_uuuu_uuuu_uuuu_uuu_____uuuu_uuuu___________uu__u_uuuuu_uu_uuu_uu__u_uuu_uuuuu_uu_uu____uuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.h +RES: ____________________________________c___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinCapabilities.h +RES: __________________c_____________________________cccc___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.cpp +RES: _____u_u_uu_u_u_u_uu_u_uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Probe.cpp +RES: _________u__uu_uuu__u_uuu__uuu____uuu_u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Laser.cpp +RES: _______________uuu_uu_u__u_u_u_uu_u_u__uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Laser.h +RES: ____________________________uu_u_u___uuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Generator.cpp +RES: ____________uuu_uuuuu_uuu_u_uuuu__uu_uuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\MyIOStream.h +RES: _________uuuu_cccc_cccc_cccc_cccc______uuuu_uuuu_uuuu_____u_u_________u__uuu_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Generator.h +RES: ____________________u_uuuuuu_______uu____uuuu_uuuu_uuuuuuuuuuu_uu_uuuu_uu_uu_u__u_uu_uu_u__uu_uuuu_uuuuu_uuuuuuuuuuuuuuu_uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\ledc.cpp +RES: _________cuuuuu______u__c_uuuuuuuuuuuuuuuuuu_uu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Logging.cpp +RES: ___________ccc_______ccc_cccc_ccc +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Jog.cpp +RES: _____________u__uuuu_u__uu__uuuuu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StackTrace\AssertionFailed.cpp +RES: __________________________________________c_cccc___ccccc_cc__cc__c___u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Planner.cpp +RES: ________________________uuu_uu____________uuuu_uu__uuu_uuu__________________________________________________________________u_u_uu______u_uuu_uu_uuuuu_uu__uuuuuu__u___uuuuu___uu_uuu______uu_uuu_uuuu_uuuuuu__uuu_uu_u_u__uuu__uuu_uu_uuuu_uu__uuu___uuuuuuu_uu__uu_uu___u_uuuu__uu_u__uu__uuuuuuuuuu____u_uuuuuuu_____uuuu_uu___u____uuuu_uu_u____uu______uuu_uuuuuu___u__uuu______________________uuuuu_u_uuu_uuuuuu______uuuu_uu_uu_u_uu__u__uuuuuu___uuuuu_u___u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.h +RES: _______________c__uuuu_u_u_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WifiConfig.h +RES: __________________uuuuuuu_uu_______________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\VFDSpindle.h +RES: _________________________uuu_____________________________________uuu_u__uu______u______________uuuu_uuu_uu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.cpp +RES: ____________________________uuuu__u__uuu_u_u____u__uu_uu_____u_uuu_uuu___uu__u_____u_uu__uu_u___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WebSettings.cpp +RES: ______________________________uu_uuuuu_______uuuuuu__uu_uu__u_uuuu_uu_u_uuuu__uuuuuuuu_u_u_uuu__uuu_uuu_uu__uu_uuu___u_u__uuu_uuuuu_uuuu_uuuuu_______________uuuuu_uuuu_uuuuuu_uuuuuuu__u_u_uuu_uuu_u__uu_uuuuu_uuu_uuuu__uuuu__uuu_uuu_uu_uuuu_uuu__uuuuu_uuu__uu___uuuuu_uuuu_uuuuuu_u_uuu_uuu_uu_uuuu_uuu_uuu_uuuuu_uuu_uuuu_uuuu_uuuuu_u_uuu_uuuuu_uuuu_uuu_uuuuu_uuu_uuuuu_uu_uuu_uuuuuuuuu_uu_u_uu_uu__uuuu_uuuu_uuuuuuuuuuuuuuuu_uuuu_uuuuuuuuuu_uuu_uuu_uu_u_uuu_uuu_uuu_uuuuu_uuu___uu__uu_uuu_uuuuuuuuu_uuuuuuuuuuu_u_uuu_u_____________________u_uu____uuuu_uuuuuuu_uuuuu_u_uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Settings.h +RES: _______________________________________________________________________uuuuu_________u______u________u_________u___u_uu__u_________uu_uuuu_u________uu___u____u__________________________________________u________u_u_u_u_uu_u_u____________________________________u__c______________________________________u____________________________________u_______________uu______________uu___________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\JSONEncoder.h +RES: _______________________________________u___________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Kinematics.h +RES: ___________________________________u______________u___________________uuu_____u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Macros.h +RES: ______________________uuu_uuu____uu_uu_uuu_uuu___uuu_____uuuuuuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Dynamixel2.cpp +RES: ______________________________________uuu_uuuuu_uu___uu_u_u_uuu__uu__uuu_uu_u_uu_u_uu_uuuuuu_uuu__uu_u__uu_uu__u_uu_uuuu_uuu__u___uu_u_uuuuu_uuu__u_u___uuu__uu_uuuu_uu_u_____u_uu_u_u_uu__u_uu_u_u_u_uuu_u_uu_uuuuu_uu_uu_uuuuu__uuuu_uuuu_u____u_uuuuu_u_uu_uuu_uu_uu_uu_uu_uu_uu_u____u_u_u_u___u_uu__u__u_uuuuuu_u__________uu_uuu__uu_uu___u_uu_uuu__u__uuuuuuuuuuuuuuuu__uuuu_uu___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinDetail.cpp +RES: _______uu_uuuu_uu_uu_u__uuuu_uuuuu_uuuu_u___uuuu_u_u_uuuuuu__u__uuu_u_uuuuuuuu_uuuu_uu_uuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\SPIBus.h +RES: ___________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\InputBuffer.cpp +RES: _______c_c_uuuu_uuuu_u_u_u_uuuuu_uuuuuuu_uuu_uuuuu_uuuuuu_uuuuuu_uuuu_u_u__u_uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\MachineConfig.cpp +RES: _________________________________uuuu_uuuuuuuuuuuuu_u_uuuuuuu_uu_uuuu__uu__uu__uu__uu__uu__uu__uu_____uu__uu__uu____u_uuuu_u_uu_u___u___uuuuuu__uuu__uu_uuu_uuuu__uuuuuu_uuuu_uuuu_uuuuu____uuu_u_u_u_u_uuuuu_u_uuuuu___u_uu__uuu_u_uuu_uuu_uuu_u_uu_cccccccccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepping.h +RES: _______________________u________________________u_uuuu_u__________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\NullSpindle.h +RES: ______________________________u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\HandlerBase.h +RES: _______________________________________uuuuu_uuuuu_______________pp_ppp_puu__p__uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StringRange.h +RES: ______________u_c_c____u__________uuuu_uuu_uu__uuuuuuuuuu__uu_uuuu_uu_uu______cccccc_c__cu_c_ccuucccccc_c_u_uuu_u_uuu_u_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\CoolantControl.h +RES: _____________u______uu___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Probe.h +RES: ___________________u______u______u___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\MachineConfig.h +RES: __________________________________uu_____u__u_uuuuu__u_____ccccccccccccc_c_cccc_c_____c__c_ccc_u_uu______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\MotionControl.cpp +RES: ______________________________uuu______________uu_u__uuu_________________uu___uuuuu_u__uuu_uuu_uuu_u____uuuu________________uuuuuuu_u_uuuu__uuuu_uuu_______u_u___uuu_u_uuuu__________________________uuu____uuuu_uuuuu__uuuuu__uuuuuuuuuuu_uu_u__uu__uuu_uuu____uu__u_____uu___u_uuu____uu_uu_______uuuu__uu__uuu__u__uuuu__uuuuu__uu_u_u_uuuuu_u_u___uuuuu_uu_uu_uuuu_u_uuuu_u___uuu_uuuuuu_uuu_uuuuu_u_u_uuu_uu_______u_uu_____u_uuu_uu_u_u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\JSONEncoder.cpp +RES: ________u________u______uuuu_uu____uuu_uu__uuuuu_____uuu_uu__u__uuuuuu_u__u___uuuu_u__uuuuu__uuuuuu__uuuuu___uuuuu__uuuuu__uuuu__uuuu__u___uuu_____uuuuu___uuu___uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Dynamixel2.h +RES: _________________________________________________u_____________________________________uu_____u_________uuuuu_uu_uu_u_u_uuuu__u_u__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinDetail.h +RES: _______________u___________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Main.cpp +RES: _____________________________________uuuu_uuu_uuu_u_uu_uuuuu_uuu_u___uuu___uuu___u_u____________________________________________u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.cpp +RES: _______cu_u_uu_uuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.h +RES: ________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Limits.cpp +RES: __________________u________________u_____uuu_uuuuu_uu____uuu_uuuuuu_uu__uu____uu_uu_uuuuuuu_uu___uu_uuuu_u_uuuu_u_______________________uuuuu__uu_uuuuu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinCapabilities.cpp +RES: ______c__________ccccc_cccc_cccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Protocol.cpp +RES: __________________________________c_____________c_________________________________________uuu_uuuu_u_uuuuuuuuuuuuuuuu___u________u___uuu_uuuu___uuuu_uuuu__u_________uu_uuu__uuuuuu_u_uuuuu_u_uu___uu_uu____u_uu__uuuu_____________uuu_u_u___u__uuuuu_uu_______uuu_u____________uuuu_u_uuuu____uu_u___uuuu______uuu_uu__uu_uuuuu_u_uuu_u_uuuu_u__uu____u___u__uu__uu__uu_u_______uu_u_uuu_____u___u__uu__uu__uuu_uu_u____uuu_u___uu__u_uu_u_uuuuuu__uuuu_u_uu_uu_uu__u_uu___uu_uuu__uuu__uu___u__________uu_u_uu_u_uuuuuuuuu_u___uu____u_uuuu_____u__u_uu__uuuuu____________u_uu_uu_u_uu_u_uu___uu__uu__u_uu_u_u________u__uuu_uuu___________uuuuuu_uuuuuuu___u_u_uuuuuu___uuuu___uuu___uu_uuuuu______uuuuuu__uu_uuuu__u___u_uuu__u__u_u__u_uu_u_u_uuu__uu_uu_____________________________uuu_u_uu__uuu__uu__uu__uu__uu__uu__uu__uuu_uuu_uuu_uuu__u________u_____u_____u__u______u__u_uuuuuu_u___uuuuuuuu_uu__uuu____uu__u__u_uu_u__uuuu____u__uuuuuuu__uuuuuuuu_uuuu_u__uuu__uuuuu_uuuuuuuu__uuu___u__u_uuuu___u_uu_uuuu___u_uuu____u_u___uuuuu__uuu___u__u_uuuuuuuuu_uuuu_uuuu__uu_u_u__uuu____uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axes.h +RES: _______________u___u_____________u____u_______uuu_uuuu_uuuu_______________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\lineedit.cpp +RES: _____ccc___uuu_uuuuuuuu_u_uuuuuuuuu_uuuuuu_u_u_uuuuuuuuuuuu_u_uuuuuuu_u___uu_uuuuu__uuuuu_u____uu_uuuuu__uuuuuuuu_uuuuu_u_uuu_uuuu_uuu_u___uuuu__uu_uu_uu_uuu_u___u____u_uu_uu_uuuuuu_uuuu_uu_uu_uuuu_u_uuuu_u_uuu_u_uuuu_uuuu_uuuuu_uuuuuuuuuuuuuu_uuu___uuuu_uuuu_______u__uuu__uuu_uuuuu_uuuuuuu___uuuuuuuuuuuuuu_uuu_uuu_uuu__uuu_uu_uuuuuuu__uuuu_uuu_uuuuuuu_uuu__uuuu_uuuuuuuuuuuu_uuuuuuuuuu__ccccccc_uuu_uuuuuuuu_u___uuuuuu________uuu_u__u_u_uu_uu____uu_u__uu__uuuuu_uu_uuu___uuu________uuu_u_uu_uu_u__uu___uuu__uu_uu_uu___uu_uu_uu_uu_uu_uu_uu_u______uuuuuuu____uuuuu_u_uuu___u_uu__uu_u__uuu__uuu__uu__uuu_u__uuu__uu_uu_uu_uu__uuu__uuu_uuuuu__uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\lineedit.h +RES: ______________________________c__________c___c_c__c_______________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Parser.cpp +RES: ______________c_u_uuuu_u_ccc_ccc_ccc_cc_c_uuuu_uu_uu__uu_uuu_uu_uu_uu_uu_uuu_uuuuu_uuuuu_uuuuuu_uuuu_uuuuu_uu_uuuuu_uuu_uuu_uuuu_u_u__uu__uu__uu_u___uuuuuuuuu__uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\WallPlotter.cpp +RES: _____________________uuuu_uuu_uu_uu______uuuuu_u_uu_u_u___________u_____uuu___uuuuu__u_u__u_uu___uu_uuu____u_______________uuu__uuu_u__uu_______u___u__uuu__u_______________________uuu__uu_uu__uuu__uuu_u__uuu_uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\MotorDriver.h +RES: ______________________________________u____________u_____________________________u__________u_____u__u______u___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\WallPlotter.h +RES: ________________________________u_u__u_u_____________uuu_uuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Midtbot.cpp +RES: _______u_uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\H2ASpindle.h +RES: ________________u_uu__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Midtbot.h +RES: _______________________________u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\PWMSpindle.cpp +RES: _____________________uuu_uuu__uuu__uu_u_u_uu_u_u_uuu__u__u_uuu_uuuu__uuu____uuuu_u__u_______uuuuu__uuu__uu___u_uuu___uu__u_uu______uuu____uuuuuu_uuu_uuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homing.cpp +RES: ________________________________________uuuuu_uuu_u_uuu___u__u_uu_u_u___u_uuuu_uuuuuuu_______u_uuuu_u___uuuuu_u__uuuuuuuuu_uu_uuuu_uu_u__uu__u_uu__u__u_uu__u_____u_u_u_u_uuu_u__u_u__u_uu_uuu___uuu_u__uuuuu__uu_u__uu___uuu_u__uuuuu___uu_u_uu___________u________uu__uuu_uuuu_uuu_uu_uuuuu_uuuu_u_uuuuuu__u___uu_uuuuuu__uuuu_uuu_uu__u__uuu_u_u__u___________uuu_uuuu__u_uuuuuuuu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.h +RES: __________________________________u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Commands.cpp +RES: __________________________uuuu_uuu_uuuu___uu___uuuu_uuu____u____uu_uuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\RcServo.cpp +RES: _________________________________uuuuu__u_uu_uu_u_uu_uuu_u_uu__uuu__u_uu_uuu_u__u_uu_uuu_u_uuu_uu_u_uuu_____u_uu__u____uu_uu_uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\PwmPin.cpp +RES: _____________u_____uu_____________uu___uuu_uu_uu__uu_uu__u__uuu_u__uuuuu_u__u_____uu__uu_uu_u_uu____uuu_uu_uuuuu__uuu_uu_uu__uuuu_uu__uu__uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\PwmPin.h +RES: _________________u_____________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Arduino.cpp +RES: ________uuu_uu_uuuu__u_ccc_ccc_uuuu_cccc_uuuu_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.h +RES: ________________________________uuu__u__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SoftwareGPIO.h +RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_uu_u_cc_c_uu___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cuuuu__c_cc_cuuuuuu_uc_c_u_ccc_cccc_ccccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\PinOptionParsing.cpp +RES: _______puu__uuu___uuu_uuuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuuuuu_uuuuuu_uu___uuuuuuuu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\TestFactory.cpp +RES: ______________________________________uu__uu____uu__uu_uuuu_u_uuu__u_u_uuu_u_uu_uu_uuuu_uu_uu_uu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\Undefined.cpp +RES: ______p__uu__uuu___uuu__uu_uuuu_p_uu_uu__uu_uu__uu_uu__uu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\ErrorPinTest.cpp +RES: ______c__c_cc_c_cc_pu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Extender\I2CExtenderTests.cpp +RES: ________________u_____u_c_c_____c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__c_cccccc__ccc__cccccc_c_cccccc__ccc__ccccc_cccccccccccccc_____c_cccccccc__c_cccccc__ccc_c__ccccc___________________c_cc__c_cc_uuuu___uuuu_uuuu____uuuu_uuuu__uuuu_uuuu__uu____uuuu_uuuu________u_uu__u_uu_uuuu______u_uu__u_uu_uuuu___uuuu_uuuu___uuuu_uuuuu_cc__cccccc__ccc_c__ccccc___________________c_ccc__cc_cccc___cc_cc_cc____cccc_ccccc__cccc_ccccc__cc__c___cccc_ccc_cc________c_cc__c_cc_ccc_cc______c_cc__c_cc_ccc_cc___ccc____ccc____cccccccc___ccc____ccc_c_c__ccccccccccc_cucccccu_cu_c_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c__cc_c__c_cc_ccccc_c___ccc____cc__ccc_ccuuu__u__uuuuu_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c___ccc_c____cc____cc__c__cc_c__c__cc__cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.h +RES: ______________c____c__c_____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-port.h +RES: ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________c___________cccc_c_c__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlTreeBuilder.cpp +RES: ________________uccccc_______ucccc__________cc_______uccccc________uccccc____ccc_c____c__cc____cc_cuuu_cc___cc_cuuu_cc__cc_cuu_cc__cc_cuu_cc_________cc__ccuu___uuu_uu_cc_________cc__ccuu___uuu_uu_cc_________cc__ccuu___uuu_uu__c_ccccu__uuuuu_uuuuu_uuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\NutsBolts.h +RES: ____________________________________________________________________________________________________________uuu____uuu_uuu_____uu___uu____uuu__uuu_uu_uu__uuuu___uuuuu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\ParserHandler.h +RES: _______________________cc___c______cc_______c__c____cc_c___cc__u__uu_cc_______c_________c____cc_c__c_ccuu_c_ccu_c_uuu_u_uuuu_u_uuu_u_uuu_u_ccc_c_uuuuuu_uuu_u_c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axes.cpp +RES: _________________uuuuu_uu_uuu__uuuu__u__uuuuu_u_uu_uuuuu_uu_uuuu_uu___uuu_uuuuuuuu_u__u_uu_uuu_uu_____uu_uu_uuuu_uuu___uuu_uuuu__uuuu___uuu__uuuuuuuu_uu_uu_uuuuu___uuuuu_uu_uuu_uuuuuuu_u_u_uuu___uuu____u____uuuu_uuu_u_uuuu_u_uu__uuu_uu_uuuu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StackTrace\AssertionFailed.h +RES: _____________________________c__u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlParser.cpp +RES: ______cc____cc_cc__c_cc__c_cc_ccc_cc_____cc_cc__c_c_ccc___c_cc_ccc_cc_______cc_cc__c_cc_cc___c_cc_cc___c_cc_ccc_cc__________cc_cc__c_cc__cc_cc_c_cc___c_cc_cc_c_cc____c_cc_ccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\H2ASpindle.cpp +RES: ____________________u_uuu_uuuuuu_u__uu_uuuuuu_uuuu__uuuuu___uu_uu__uu_u_uuuu_u_uuu__uuuuu___uuuuu_uuu__uuuuu_____uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\gtest.h +RES: __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________c_______c____________________________________________________________________________u__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlComplete.cpp +RES: ______cc_____________________________________cc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_cc_c_cccccuuuuuuuuu_uu_u_u_uuuuu_uu_u_uuu_uuu_uuu_uuuuuu__uu_u_uuu_uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Tokenizer.h +RES: ________________ccc_cc_cccc_c_ccu_ccc_c_c_cccc_c_cccuuu_cccc__________c_____________c_____c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\NotificationsService.h +RES: ____________u__________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.cpp +RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccu_u_u_u________c__cc______cu__c_u__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccu_uuu_ccc_c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.h +RES: ________________________________u_______uu_______________u_uu_uuu_u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\GCode.cpp +RES: ________________________uuuu_____________u_u__uuuu___uuu_u_uuuuuuuuu_u___u_u__u_uuu_u_u_uuu__u__uu___uu_______u__u_uu__u_u_u_uu_uuu_uu_______u_u__________uuu_u_uu_uuuu____u_uuuuu_________u___uuuuuu_u_uuu_uuu________uu___u____u__uuuu_u_uu__uu_uu_u___u_uu_uu_uu_uu___uu_uuu_uuu___uuuu_uuuu_uuuu_uuuu__uuu___uu_uu_uu_uu_uu_uu_u__uuu__uuu_uuu_uuu_uuu_u_uuu_u__u_u__u_u_uuu_u__uu_u__u_uuu_uuu_uuu_uuu____uu_____uu__uuuuuuu_uuu_uuu_uuu_uuu_uuu_uuu_uuu__uu__uu_u_uu___uuu_uu__uu_u__uuu__uu__uuu__uuu___u_uu_uuuu_u_u__uu_u_uu___u_uu_u_uu_u_uu___uu_uuuuu_u_uuu_uuu_uuu_uuu_uuu_uuu_u___uuu_uu______u_uuuuuu_u_uuuuuu_u_uuuuuu_u__uu_u_uuu__uuuu_uuuu_uuuu_uuu_uuu_uuu_uu_u_uuu_uuu_uuu_uuu_uuuu_uu_u_uuuuuu_u_uuuuuu_u_u__uuu___u_uu__u_u____________________________uuu___u_uu_____________uuu_uu_uu_uuuu______________u_uuuu_uu______uu_________uuuuu_u____uuu_u__uuu_u_uuu_uu__u_uuuu_uuuu_uuu____uuuu_u___________uuuu_________uu__uu_uu__________u____uu_uu_uuuu_uu___uu_uu_u_uu_uu__u_uu__uuu_u_u__uu__uu___uu_uuu_uu_uu_____uuuuuu__u_uuuu_uu___u___u____uuuu_u_uuu_uuu_u____u__u___uu_______u__uuu__uu__uu___u_uu_u_u_u___uu_u_u_______uu_uu___uuuuuu__uu__________________________________________________uuu__u_uu____________________uuu__uuuuu_u_uuuu_u__uuu_u_uuuu_uu___u__u__uu_____uu_uu________u_uuu___uu___uu________uu____u__uu_uu__uuuuuuu__u__uu_uu____uuu__uu_uu_u_uu_______uu___uuu__uu_uuuuuuuuu_u___u__uu____uu__u___uuuu_u_u______uu_u_uu_uu_u__uuuu___u__uuuu_uuu_uu__uuuuuu_uu_uu_uu____uuuu____uu__u_u______uuuu__uuu___uuuu____u__u_u_uuu_u____uuu_uuu_uu_uu_uuu_uu_______uuuuuuuuuuu________u__uu_u____uuuu______uu_u___u_uuuu_u__u_____uuuu_uuuuuuuu____uuuu___uuuuuu_uu__u__uu__________________________u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2SOBus.cpp +RES: ________uuuuu_u_uuuuu_uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\UnipolarMotor.h +RES: ______________u_____uuuuuu_uuuuuuu__u______uuuu_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\UnipolarMotor.cpp +RES: _______uuuuuuuu_uu_u_uuuuuu_uu_u_uu__uu__u_uuuu_______________uu_uu_uuu_uu_uuu_uu_uuu_uu_uu__uu_uuu_uuu_uuu_uu____uuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Channel.cpp +RES: _______uuuuuu_uuu_uu_______uuuu________uuuu_uu_uuuu_u_uu_u_uu______uuuuu_uu____uuuuu_u__u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.cpp +RES: _______cc______ccccc_ccc_ccc__c_c_c_cu___cu___cu___cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Tokenizer.cpp +RES: ___________ccuuc_c_cuuuuu_c_u_c_cc___c___c____c_ccccc_ccccc_c_uu__uuuuu__uuu_u__cc__cu__cccccc__cuu_cu_c__ccc__c_____cccccc_cccccccu_c____cccccccu______c___c_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.h +RES: ________________________c______________________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\ParseException.h +RES: ___________________uuuuuuu_uuuu_uu_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\UserOutputs.cpp +RES: ___________uuuuuu_uuuuuuu_u_u_u_uu_uuuuu_uuuuu_uu_uuuuuu_uuuu_uuu_uu__uu__u_uuuu__uu__u_u_uu_uuuuuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepper.cpp +RES: ___________________________________________________________________uuu_uuu_uu____________________________________________________________________________________________________________________u_uuuu________uu_u__u_u_u_________uu__uuu____u_____uu__________u_uuu_uu_uu_uu__uu____uu_uu___u_u_uuuuuuuu__u_uu_uu__uu__u__u_u__uu_uuuu__u_u_u__uuuuuuuuu_u__uuuuu_u__u_uuuuu__uuuu__u_uuuuuuuuuuu__uu__uuuu______________u_uu__u_u_uuuu__uu___uuuuu_u_u___uu_u____uu____u________u__uuuuu_uuuuu___u_uu_uu_________uuu__u_uu_uuuu_u_uu__uuuuu__uuuuuuu___uu___u_uuuuuu_uuuu_uu_u_uuuu_uu___uu_u___u___u______u_______________uuu__uu_uu___u_uuuu_uuuuuu_u__uuu_uuuuuu_uuu_u____uu_uuuuu_u__uu_uuuuu___uuu__uuuuu__uuuu__u____uuu_uu______uuuu_u_uu___________uuuu__uu__uuu_u____________u_u____u___uuu_uuuu__u__uuu__uuu_u_u___uuu_uu_uuu_uu__uu_____uu_____u_u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Validator.cpp +RES: _____________u_uu_uu_uu__u__uu_u_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Validator.h +RES: _____________________uu____uuuuuuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Error.cpp +RES: _______c____________________________________________________________________c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.h +RES: ________________________________________________________________________________________c____c___cc___c___c__ccccccc__________________c________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.cpp +RES: _____________________c___cuuuc_cc__uuuuu_uuu__u_u__uuu_u__uuuuuuuuu_uu_uuuuu_uuuu_uuuuu__uuuuuu_uuu_u__uu_u_uuuuu____uu_uuu_uuuuuuu_u__u_uuu_uuuuuu_u__uuuuuuuu_u_uuuuuu__uuuuu_uuuu_uuuu_uu_____uuuuuuuuu_c_uuuuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.cpp +RES: _____________________________________uuuuu_uuuuu_uuu_u_uuuu_____u_uu__uu__uuu_u_uuuu_u_uu__uuu_u__uuu_uuu_u_uuuu__uuu__uuu_uu_uu_uu__uu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.cpp +RES: ___________u_u__u_uu_uuu__uu__uuu__uuuuuuuuu____u_u_uuuuuuuu__u_uuuuuuu__u_uuuuuu_u__u_uuuuuuu__u_uuuuuuuu_u_uuuuuuuuu_u_uuuu_u_uuuuu___u_uuuuuuuuuu_uu____uuu_uuuuuuuu_uuuuuuuuu_u_uuuuuuuuu_uu_u_uuuuuuu____u_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.h +RES: __________________uuuuuuu_u___u________u_____u_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\NutsBolts.cpp +RES: _______________________uu__u_uuuuuu___uuuuuuuuuuu_uuuu_uuuuu_uu_uu____u__uuuuuuuu_uu___uuuu_uuu_uuu_uuu__uuuuuu_uuu__uu_uuuu__uuu_uuuuuu_uuuuuuuu___uuuuuuu_u____uu_uuuuuuu_uuu_uuu_u__uuuuu__uuuu_uuu_uuu_uuuu_uuu_uuu_uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\InputFile.h +RES: _____________________________________________________u___u______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\HuanyangSpindle.cpp +RES: _______________________________________________________________________________________________________________________________________________u__u_u_uu__uu_u_uu_uu_u__u_u____uu______________________uu__uuuuu__u_uu__uu_uu_u_u_uu__uuuuu_u_uu__uu_u____uuu_u_uu__uu_u_______u_uuu_uu_uuu_u__u_u_u_uuuu_uu_u_uu_uuuuu_u_uu_uuuu______uu_u_______uu_u_uuu_uuu_u_uu__uuuuu_uuuu_uu_u_uu__uuuuu_uu__uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\HuanyangSpindle.h +RES: ______________uuuu__________u__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.cpp +RES: _________ccuuu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.cpp +RES: _________________uuu__u_uuuu__u_u_uu_u_uu_uuu_uuuu_u_u___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.h +RES: _____________________________________u_u________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\CoreXY.cpp +RES: _____________________________u_u__uuuuu_uu__u_uuuuuuuuuu___uu__u__uuuuuu_u___u_u__uuuuuuuuuu_u_uuuu______u_uu__u_____uu_uu_u_uuu_u_u__u_uu_uuu_u_uuu__uuu__uuu_u_uu____uuu_uuuu_u__uu__uu_uu__uuuu_uuuuuu_uuuuuuu__u_u_u__uu_uuu_u__u__uuu_u_uu_uuu__u_uu_uu_u_u___________u_____uuuu_u__u_u_uu__u__uu_______u__uu_uuuu____uuu_uuuuu___uu_u___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\CoreXY.h +RES: ____________________________________u_u__u_u___________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepping.cpp +RES: __________ccccc_uu_____uu__uu_____uu_uuu_uuuuu_uuuu__uu_u_u_uuu_u__uu__u_uu_uuu_u__u__u_u_uuuuuu_u__uuu_u___uu___uuu_u_uuuuuu_u_uuuuu_u_____________u___u_u______u_uu_uuuuuuuu_uuuuuu__u_uu__u_u__u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\xtensa\core-macros.h +RES: ____uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\OnOffSpindle.cpp +RES: _______uuuu__uuu_u_u___u_uuu__uuu_uuu____uuuu_u__u_uuuu_u_u_uuu__uu_u_uuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Kinematics.cpp +RES: __________uuuu_uuuu_uuuu_uuuu_uuuu_uuu_u_uuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\InputFile.cpp +RES: _________u______uuu_uuu_uu_uu_uuuuu__uuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Servo.cpp +RES: __________________________uuuu_uuu__uu________u_u_uu_uuuu_uuu_u_____uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Servo.h +RES: ________________u__________u_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ProcessSettings.cpp +RES: _________________________________uuu_u_u_uu_u_u_u___uu_uu_uu_uu_uu_uu_u__uuu____uu_u_uuuuu__uuu_uuuu_u_uuu_uu_uuu_uuuu_uu_uuu__uuuuuuuuu__uu_uuuuuu_uu__uuuuu________uuuuu_uuuu_uuuu_uuu__u_uuuuuuuuuuu_uuuuuuuu_uuuuuuuuu__uuuuuuuuuuuu_uuu_uuuuuuu_____uuuuuuu_uu_uuu_uuu_uuuu_uuuuu_uuuu_uu__uuuuuuuu_uuuuuuu_u__uu_uu__uu__u_u_u_u_uuuuu__uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_uuuuuuuu_uuuuuuuuuuuuuuu_uuuuuuuu_c____cuuu_uuu_uuu_u_uuu_uuu_uuu______uu__uuuu_uuuu_uuuuu_uuuuuu_uuuuuuu___uuuuu_uuuu_uuuuuuu_uuuuuuu___uuuuu_uuu__uuuuuuu__u_uuu__uu_uuu_uuu_uuu__uu_uuuuuuuuuu_uuu_uuu__uu_uuuuuuuuuu_uu_u_uu_uuu_uuuuuuu_uu_uuu_uu_uuuu_uuuu______uuuuuuuu_uuuuuuuuuuuuuuuu_uuuuuu_uuuu_u_uu______u_____uuu__uu_____u___u_uu___u________uuu_uu_uuuuuuuu_uuuuu_uuu_uuu___uuuu_uuuuu__u___uuuu_uuuuu__u___uuuu_u_u_____uuuuuuuu_uuuu_uuu_uuu_uu__uuu_u__u_uu_u_uu_u___u______uu_u_uuuu___uuuu_uu_uu_uu__uu__uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.h +RES: _____________________uu____uuuuuuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axis.cpp +RES: _______uuuuuuu__uu_uuuuuu_uuuuu_uuuuuu_uuuu____uuu_u_uuuuuuu__uuuuu_uuu__u__uuuuuu_uuu___u_uuuu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\FileStream.h +RES: ___________________u_______uuuuu_______u_________u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Motor.cpp +RES: ____________uuuuuuuuuuu_uuu_u_uuu_u_uuuu_uuu_u__u__uuuuu_u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.h +RES: _________u_u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\JsonGenerator.cpp +RES: ____________uu_uuuu_uuuuuuuuuuu_uuu_u_uuuuu_uuuuu_uuuuu_uuuu_uuuu_uu_uuuuu_uu_uuuu_uuuu_uu_u_uuuuuu_u_________u_uuuuuu_uuu_uu_uu_u_uuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\JsonGenerator.h +RES: _______________________________uu_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Settings.cpp +RES: _____________uuuuuuuuuuuu__u_____uuuuu_____uuuu___uuuu_uuuuuuu_u_uuu_uu_uu___uuuu__u___________uuuu_uuuuuuu_u_uuuuuuu__u_uuuuu__uuu_uu___uu__uuuuuu_u__uuu_u_uuu_u___uuuuu_uu__uuu_uuuu_u__________uuuuuu_uuuuuuu___uuuuuuuu_uuu_uuuu_u_uuuu_uuu_uuuuuuuu_u__uuu_uuu_uu_u_uuuuu_uuu_uuu___________uu_uuuuuuu_u_uuuu_u_____uuuuu_uu___uuu__u_uuu_uuu_uuu__uuuuuuu_u__uuu_uuuuuuuuuuuuuuu_uuuuuuu_uuu_uuuuuuuuuu_uuu_uu__u_u_u_______u___u_u_uuuu_uu________u_u________uuuuuuu_u_uuuuuuu_u_uuuu_u_uuuuu_uuu_uuuuuuu_u__uuu_uuuuuuuuuu_uuuu_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homing.h +RES: _________________________________uuuuuuuuu__u_uuuuuuuuuuu_u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Motor.h +RES: ___________________________u_uu____u____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ControlPin.cpp +RES: _______uuuuu_u_uuu_uuuu_uuuu_uuu_u_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\FileStream.cpp +RES: _________uuuu_uuuuuuu_uuu_uuu_uuu_u_uu_u_uu_u_uuuu_uu_u__uuu_uuuuuuu__u__uu___u__uuuu_u_uu_uuuu_u_u_uuuu_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axis.h +RES: ____________________uuuuu____u_uuuuu_________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\EnumItem.h +RES: _____________c__c____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SettingsDefinitions.cpp +RES: ___________c________c_c_uuuuuu_u_uu___uuuuuuuu_u_u__u_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\PWMSpindle.h +RES: __________________________________u_u____________u_uu__u_u________u________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\OnOffSpindle.h +RES: ____________________uuuuu_uu____________________u_uuuu_u__u______u_u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\System.cpp +RES: ____________________u_uuuuuuuuuu_uuuuuu_u_uuuuuuu_u_uuu_c__________c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.h +RES: ___________________________________________________cu___________________________________u_____________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.cpp +RES: __uuuuuuuu_c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.cpp +RES: _________ccccc_c_uuuuuu_ccc_c_cccu_c_cc_uu_u_u_u_u_u_u_u_u_u_u_uc___cccccc_c___ccc_cccc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.cpp +RES: _______________c_c_cc_____cu_uuuccc__ccc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c___c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_uu_uuu__uuuuuu_u_u__uuu_u_c_c__cc___ccc__cc___c_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.h +RES: ______________________c___________________cc___c___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.h +RES: ______________________________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\Extenders.cpp +RES: ______p_uuu_uuuuu_u_u_uuuuu_u_uu_uu_uuuuuuu_uuuu_uu_uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Configurable.h +RES: _____________________u_u__c__ +PROF: diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index b7dea07c7..4dbc6ee83 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -17,21 +17,34 @@ namespace Extenders { I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} - uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + uint8_t I2CExtender::I2CGetValue(uint8_t address, uint8_t reg) { + auto bus = _i2cBus; + + // First make sure the bus is empty, so we read that which we have written: + // while (bus->read(address, ®, 1) != 0) {} + int err; if ((err = bus->write(address, ®, 1)) != 0) { log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + + IOError(); return 0; } else { uint8_t result = 0; if (bus->read(address, &result, 1) != 1) { log_warn("Cannot read from I2C bus: " << "no response"); + + IOError(); + } else { + errorCount = 0; } return result; } } - void I2CExtender::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + void I2CExtender::I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { + auto bus = _i2cBus; + uint8_t data[2]; data[0] = reg; data[1] = uint8_t(value); @@ -40,16 +53,21 @@ namespace Extenders { if (err) { log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + IOError(); + } else { + errorCount = 0; } } - void I2CExtender::isrTaskLoopDetail() { - std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); - int registersPerDevice = _ports / 8; - int claimedValues = 0; - uint8_t commonStatus = _operation; + void I2CExtender::IOError() { + if (errorCount != 0) { + delay(errorCount * 10); + if (errorCount < 50) { + ++errorCount; + } + } - // Update everything the first operation + // If an I/O error occurred, the best we can do is just reset the whole thing, and get it over with: _status = 1; if (_outputReg != 0xFF) { _status |= 4; // writes @@ -57,10 +75,22 @@ namespace Extenders { if (_inputReg != 0xFF) { _status |= 8; // reads } + } + + void I2CExtender::isrTaskLoopDetail() { + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + int registersPerDevice = _ports / 8; + int claimedValues = 0; + uint8_t commonStatus = _operation; + + // Update everything the first operation + IOError(); // Main loop for I2C handling: while (true) { - uint8_t newStatus = 0; + // If we set it to 0, we don't know if we can use the read data. 0x10 locks the status until we're done + // reading + uint8_t newStatus = 0x10; newStatus = _status.exchange(newStatus); newStatus |= commonStatus; @@ -89,9 +119,10 @@ namespace Extenders { if (_invertReg != 0xFF) { uint8_t currentRegister = _invertReg; uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { uint8_t by = _invert.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); currentRegister++; if (currentRegister == registersPerDevice + _invertReg) { @@ -101,12 +132,12 @@ namespace Extenders { } // Configuration: { - for (int i = 0; i < claimedValues; ++i) { - uint8_t currentRegister = _operationReg; - uint8_t address = _address; + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { uint8_t by = _configuration.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); currentRegister++; if (currentRegister == registersPerDevice + _operationReg) { @@ -138,7 +169,7 @@ namespace Extenders { for (int i = 0; i < claimedValues; ++i) { if ((toWrite & (1 << i)) != 0) { uint8_t by = handleInvertSoftware ? (_output.bytes[i] ^ _invert.bytes[i]) : _output.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); } currentRegister++; @@ -158,7 +189,7 @@ namespace Extenders { for (int i = 0; i < claimedValues; ++i) { auto oldByte = _input.bytes[i]; - auto newByte = I2CGetValue(this->_i2cBus, address, currentRegister); + auto newByte = I2CGetValue(address, currentRegister); if (handleInvertSoftware) { newByte ^= _invert.bytes[i]; } @@ -166,7 +197,7 @@ namespace Extenders { if (oldByte != newByte) { // Handle ISR's: _input.bytes[i] = newByte; - int offset = claimedValues * 8; + int offset = i * 8; for (int j = 0; j < 8; ++j) { auto isr = _isrData[offset + j]; if (isr.defined()) { @@ -181,13 +212,15 @@ namespace Extenders { } currentRegister++; - if (currentRegister == registersPerDevice + _invertReg) { + if (currentRegister == registersPerDevice + _inputReg) { ++address; } } } } + _status &= ~0x10; + vTaskDelay(TaskDelayBetweenIterations); } } @@ -334,7 +367,7 @@ namespace Extenders { _status |= 8; } while (_status != 0) { - vTaskDelay(1); + vTaskDelay(1); // Must be +#include #include #include #include @@ -8,6 +9,26 @@ #include "Capture.h" +#include + +namespace { + struct GPIONative { + // We wire 15 to 20. + static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { + // switch (pin) { + // case 20: + // pins[15].handlePadChange(value); + // break; + // } + } + + inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, true); } + inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } + inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } + inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } + }; +} + namespace Configuration { Test(I2CExtender, I2CBasics) { // Initialize I2C bus @@ -147,13 +168,16 @@ namespace Configuration { Roundtrip() { before = Capture::instance().current(); } ~Roundtrip() { - while (Capture::instance().current() < before + 1) { - delay(10); + for (int i = 0; i < 10; ++i) { + while (Capture::instance().current() < before + 1) { + delay(10); + } + before = Capture::instance().current(); } } }; - Test(I2CExtender, SetupPin) { + Test(I2CExtender, ExtenderNoInterrupt) { // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -191,8 +215,6 @@ namespace Configuration { // high = 1, low = 0 { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -202,8 +224,8 @@ namespace Configuration { i2c.setupPin(0, Pin::Attr::Output); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -219,7 +241,7 @@ namespace Configuration { { Roundtrip rt; } auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv.size() == 1, "Expected single data request / response roundtrip, got %d", int(recv.size())); Assert(recv[0] == 0, "Expected read"); Assert(readPin == true, "Expected 'true' on pin"); } @@ -269,8 +291,6 @@ namespace Configuration { // Setup pin for reading: { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -280,8 +300,8 @@ namespace Configuration { i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -292,8 +312,6 @@ namespace Configuration { // Setup another pin for reading with an invert mask and a PU: { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -303,8 +321,8 @@ namespace Configuration { i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -337,4 +355,428 @@ namespace Configuration { Assert(readPin == false, "Expected 'true' on pin"); } } + + Test(I2CExtender, ExtenderWithInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender with ISR on gpio.15 + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(true); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + // Expected register values (see datasheet): + // + // 4 invert + // 1 = invert, 0 = normal + // + // 6 config + // 1 = input, 0 = output + // + // 2 write + // 1 = high, 0 = low + // + // 0 read + // high = 1, low = 0 + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x01); + + i2c.claim(0); + i2c.setupPin(0, Pin::Attr::Output); + { Roundtrip rt; } + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), + uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Read will NOT trigger an update because we have an ISR to tell us when it changes: + { + bool readPin = i2c.readPin(0); + auto recv = Wire.Receive(); + + Assert(recv.size() == 0, "Expected single data request / response roundtrip, got %d", int(recv.size())); + Assert(readPin == true, "Expected 'true' on pin"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Test write pin: + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'low'. + i2c.writePin(0, false); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 0, "Expected write reg 0 = 0"); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'low'. It's already low, no-op. + i2c.writePin(0, false); + i2c.flushWrites(); + // no-op. + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // NOTE: We ended with setting pin #0 to 'high' = 0x01 + + // Setup pin for reading: + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + + i2c.claim(1); + i2c.setupPin(1, Pin::Attr::Input); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), + uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Setup another pin for reading with an invert mask and a PU: + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x04); + + i2c.claim(2); + i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), + uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(1); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Trigger an ISR, change both pins + { + Wire.Send(0x02); + GPIONative::write(15, true); + GPIONative::write(15, false); + { Roundtrip rt; } + auto recv = Wire.Receive(); + Assert(recv.size() == 1); + Assert(recv[0] == 0); + } + + // Test read pin: + { + bool readPin = i2c.readPin(1); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + } + + void HandleInterrupt(void* data) { ++(*reinterpret_cast(data)); } + + volatile uint16_t currentInput = 0; + void WireResponseHandler(TwoWire* theWire, std::vector& data) { + if (data.size() == 1) { + if (data[0] == 0) { + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(currentInput)); + data.clear(); + } else if (data[0] == 1) { + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(currentInput >> 8)); + data.clear(); + } else if (data[0] >= 2 && data[0] <= 7) { + // ignore until next roundtrip + } else { + Assert(false, "Unknown register"); + } + } else if (data.size() == 2) { + if (data[0] >= 2 && data[0] <= 7) { + data.clear(); + } else { + Assert(false, "Unknown register"); + } + } else { + Assert(false, "Unknown size"); + } + } + + Test(I2CExtender, ISRTriggerWithInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(true); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + uint32_t isrCounter = 0; + + { + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + { Roundtrip rt; } + + // Test read pin: + { + bool readPin = i2c.readPin(9); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Change state, wait till roundtrip + { + Wire.Send(0x00); + Wire.Send(0x02); + + // Trigger ISR pin 'falling' + GPIONative::write(15, true); + GPIONative::write(15, false); + { Roundtrip rt; } + + auto recv = Wire.Receive(); + Assert(recv.size() == 2); + Assert(recv[0] == 0); + Assert(recv[1] == 1); + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin = i2c.readPin(9); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + } + + Test(I2CExtender, ISRTriggerWithoutInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + uint32_t isrCounter = 0; + + { + // From this point on, we just need to respond to wire requests + currentInput = 0x0000; + Wire.Clear(); + Wire.SetResponseHandler(WireResponseHandler); + + i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); + } + + // Test read pin: + { + bool readPin = i2c.readPin(9); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Change state, wait till roundtrip + { + currentInput = 0x0200; + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin = i2c.readPin(9); + Assert(readPin == true, "Expected 'true' on pin"); + + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin2 = i2c.readPin(9); + Assert(readPin2 == true, "Expected 'true' on pin"); + } + + Wire.Clear(); + } } diff --git a/FluidNC/test/Pins/GPIO.cpp b/FluidNC/test/Pins/GPIO.cpp index 39ec3d49d..e9dd09635 100644 --- a/FluidNC/test/Pins/GPIO.cpp +++ b/FluidNC/test/Pins/GPIO.cpp @@ -8,38 +8,41 @@ extern "C" void __pinMode(uint8_t pin, uint8_t mode); extern "C" int __digitalRead(uint8_t pin); extern "C" void __digitalWrite(uint8_t pin, uint8_t val); -struct GPIONative { - inline static void initialize() { - for (int i = 16; i <= 17; ++i) { - __pinMode(i, OUTPUT); - __digitalWrite(i, LOW); +namespace { + struct GPIONative { + inline static void initialize() { + for (int i = 16; i <= 17; ++i) { + __pinMode(i, OUTPUT); + __digitalWrite(i, LOW); + } } - } - inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); } - inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); } - inline static bool read(int pin) { return __digitalRead(pin) != LOW; } -}; + inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); } + inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); } + inline static bool read(int pin) { return __digitalRead(pin) != LOW; } + }; +} #else # include -struct GPIONative { - // We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17: - static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { - switch (pin) { - case 16: - case 17: - pins[16].handlePadChange(value); - pins[17].handlePadChange(value); - break; +namespace { + struct GPIONative { + // We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17: + static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { + switch (pin) { + case 16: + case 17: + pins[16].handlePadChange(value); + pins[17].handlePadChange(value); + break; + } } - } - - inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); } - inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } - inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } - inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } -}; + inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); } + inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } + inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } + inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } + }; +} void digitalWrite(uint8_t pin, uint8_t val); void pinMode(uint8_t pin, uint8_t mode); int digitalRead(uint8_t pin); diff --git a/X86TestSupport/TestSupport/SoftwareGPIO.h b/X86TestSupport/TestSupport/SoftwareGPIO.h index 061578f9b..7332c1c98 100644 --- a/X86TestSupport/TestSupport/SoftwareGPIO.h +++ b/X86TestSupport/TestSupport/SoftwareGPIO.h @@ -128,7 +128,7 @@ class SoftwareGPIO { pins[index].handlePadChangeWithHystesis(value); } } else { - pins[index].driverValue = value; + pins[index].handlePadChange(value); } } diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 69373b035..2ada77bbe 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -1,10 +1,11 @@ #include "Wire.h" +#include + TwoWire Wire(0); TwoWire Wire1(1); -TwoWire::TwoWire(uint8_t bus_num) {} -TwoWire::~TwoWire() {} +TwoWire::TwoWire(uint8_t bus_num) : handler(nullptr) {} // For unit tests: void TwoWire::Send(std::vector data) { @@ -13,17 +14,21 @@ void TwoWire::Send(std::vector data) { } } void TwoWire::Send(uint8_t value) { + std::lock_guard guard(mut); receivedData.push_back(value); } std::vector TwoWire::Receive() { - std::vector data; + std::lock_guard guard(mut); + std::vector data; std::swap(sentData, data); return data; } void TwoWire::Clear() { + std::lock_guard guard(mut); sentData.clear(); receivedData.clear(); + handler = nullptr; } // TwoWire interface: @@ -81,6 +86,8 @@ uint8_t TwoWire::endTransmission(void) { } size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { + std::lock_guard guard(mut); + auto available = receivedData.size(); if (available > size) { available = size; @@ -113,8 +120,15 @@ uint8_t TwoWire::requestFrom(int address, int size) { } size_t TwoWire::write(uint8_t ch) { - Assert(inTransmission, "Should be in a transmission"); - sentData.push_back(ch); + { + Assert(inTransmission, "Should be in a transmission"); + std::lock_guard guard(mut); + sentData.push_back(ch); + } + + if (handler) { + (*handler)(this, sentData); + } return 0; } @@ -125,9 +139,11 @@ size_t TwoWire::write(const uint8_t* buf, size_t size) { return size; } int TwoWire::available(void) { + std::lock_guard guard(mut); return receivedData.size(); } int TwoWire::read(void) { + std::lock_guard guard(mut); if (receivedData.size()) { auto result = receivedData[0]; receivedData.erase(receivedData.begin()); @@ -137,6 +153,7 @@ int TwoWire::read(void) { } } int TwoWire::peek(void) { + std::lock_guard guard(mut); if (receivedData.size()) { auto result = receivedData[0]; return result; @@ -146,5 +163,7 @@ int TwoWire::peek(void) { } void TwoWire::flush(void) {} +TwoWire::~TwoWire() {} + extern TwoWire Wire; extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index 1bda1ea24..b28f807ca 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -2,6 +2,8 @@ #include #include +#include + #include "esp32-hal-i2c.h" #include "Stream.h" #include "../FluidNC/test/TestFramework.h" @@ -10,6 +12,10 @@ class TwoWire : public Stream { bool inTransmission = false; std::vector receivedData; std::vector sentData; + std::mutex mut; + + using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data); + ResponseHandler handler; public: TwoWire(uint8_t bus_num); @@ -18,8 +24,11 @@ class TwoWire : public Stream { // For unit tests: void Send(std::vector data); void Send(uint8_t value); + size_t SendSize() { return receivedData.size(); } std::vector Receive(); + size_t ReceiveSize() { return sentData.size(); } void Clear(); + void SetResponseHandler(ResponseHandler handler) { this->handler = handler; } // TwoWire interface: From be1fe140ff549c66c09a9cddfc8696143a3a0777 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 17:35:00 +0100 Subject: [PATCH 074/100] Small fixes --- FluidNC/src/Extenders/I2CExtender.cpp | 1 + FluidNC/src/Main.cpp | 88 --------------------------- 2 files changed, 1 insertion(+), 88 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 4dbc6ee83..9a378b36b 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -219,6 +219,7 @@ namespace Extenders { } } + // Remove the busy flag, keep the rest. _status &= ~0x10; vTaskDelay(TaskDelayBetweenIterations); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 06e92cd44..d5ec53066 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -28,51 +28,6 @@ extern void make_user_commands(); -// FOR TESTING: - -# include -# include - -extern "C" void __pinMode(pinnum_t pin, uint8_t mode); - -uint8_t I2CGetValue(uint8_t address, uint8_t reg) { - Wire.beginTransmission(address); - Wire.write(reg); - auto err = Wire.endTransmission(); // i2c_err_t - - if (Wire.requestFrom((int)address, 1) != 1) { - Uart0.println("Error reading from i2c bus."); - return 0; - } - uint8_t result = Wire.read(); - return result; -} - -void I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { - uint8_t data[2]; - data[0] = reg; - data[1] = uint8_t(value); - - Wire.beginTransmission(address); - for (size_t i = 0; i < 2; ++i) { - Wire.write(data[i]); - } - auto err = Wire.endTransmission(); // i2c_err_t ?? - - if (err) { - Uart0.println("Error writing to i2c bus; PCA9539 failed. Code: "); - Uart0.println(int(err)); - } -} - -volatile bool fired = false; - -void isrHandler() { - fired = true; -} - -// --- Until here. - void setup() { try { uartInit(); // Setup serial port @@ -83,49 +38,6 @@ void setup() { WebUI::WiFiConfig::reset(); - /* TEST STUFF! */ - - /* - // THIS WORKS: - { - Uart0.println("Basic test of pin extender."); - // Wire.begin(sda , scl, frequency); - Wire.begin(13, 14, 100000); - - Uart0.println("Setup pins:"); - - // 1. Setup pins: - I2CSetValue(0x74, 6, 0xFF); // All input pins - I2CSetValue(0x74, 7, 0xFF); // All input pins - - __pinMode(36, INPUT); - attachInterrupt(36, isrHandler, CHANGE); - - // 2. Read input register: - Uart0.println("Main loop:"); - while (true) { - auto r1 = I2CGetValue(0x74, 0); - auto r2 = I2CGetValue(0x74, 1); - uint16_t v = (uint16_t(r1) << 8) | uint16_t(r2); - Uart0.print("Status: "); - for (int i = 0; i < 16; ++i) { - uint16_t mask = uint16_t(1 << i); - Uart0.print((v & mask) ? '1' : '0'); - } - - if (fired) { - Uart0.print(" ** ISR"); - fired = false; - } - - Uart0.println(); - - delay(1000); - } - } - */ - /* END TEST STUFF */ - display_init(); // Load settings from non-volatile storage From ca573e6d2f0dae5ecbd1f576d31aec0b14799360 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Mar 2022 14:03:35 +0100 Subject: [PATCH 075/100] Fixed unit tests and a few more small bugs. --- CodeCoverage.cov | 54 +- FluidNC/src/Extenders/I2CExtender.cpp | 7 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 554 +++++++++++---------- X86TestSupport/TestSupport/Wire.cpp | 5 +- X86TestSupport/TestSupport/Wire.h | 8 +- 5 files changed, 331 insertions(+), 297 deletions(-) diff --git a/CodeCoverage.cov b/CodeCoverage.cov index a3f4cc636..8c6613e6f 100644 --- a/CodeCoverage.cov +++ b/CodeCoverage.cov @@ -14,13 +14,13 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Stand RES: _______________________u_uuuu_uu_uuuu_u_uu_uu_u_uuuuuuuuuuu_uuuu_uuu__uuuuuuuuuuu__uu_uuu_uuuuuu_u_uuu_u_u_u___c__uuuuuuu__uuuu___u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.h -RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________ccccuuuu__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ +RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________cccccccc__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Authentication.cpp RES: ___________________________________________u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.cpp -RES: ____cc_c__ccccccccccccccc_cccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_u___ +RES: ____cc_c__ccccccccccccccc_ccccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_c___ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.cpp RES: _______uuuu_ @@ -35,25 +35,25 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSuppo RES: ____cuuuuuuuuu_c__c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.h -RES: ___________c______________c_c_c_________________________________________________ +RES: ___________c_______________c___cccc_________________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.cpp RES: ______________________________u_u_uuu_uuu__u_u_uuu_uuu___u_uuuu_uu_uu_uuu_____uuuuuu__uuu___uuuuuuuu_uuu_________________________________________________________________________________________uuu___uuu__u_uu___u_uuuu_uuu_uu_uu__uuu___uuuu_u_u_uu___u_uuuuuuuu__uuu_uu_uuuu_u______uuuuuu_uuuuu_____uuu_uuuuu_uuuuu_uuuuuuuuu_uuuuuuuuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPIFFS.cpp -RES: ____cu___uuuuuuu__c +RES: ____cc___uuuuuuu__c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.cpp RES: ____________c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.cpp -RES: ________c_u____________cccuucc___u_u_ +RES: ________c_c____________cccuucc___u_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.cpp RES: ______________________________________ccccccc_u_u__uuuuuuu_uuuuu_u_uuuu_uu_uuu_ccc_uuu_uuu_ccc_ccc_cccuu_cc_ccuuc_c_uuuuu_uu_uuuuu_u_uuu_uuu_uuuu__uuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___c_c_c__cu___cc_cc_cc_u_u_u__uu___uuu_uu_uu_uu_uu_uu_uu_uu___uuu___uuuu_u__uuu__uu___uuuuuu_uu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.h -RES: ___________________________________________________________________________________c______c______c________u__cccc__uu_cc___cccc____c_c_u_uu_c____c_c__u_c___u___ +RES: ___________________________________________________________________________________c______c______c________u__cccc__cc_cc___cccc____c_c_u_cc_c____c_c__c_c___u___ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\rmt.cpp RES: ____uuu_u_uuu_uu @@ -86,7 +86,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuratio RES: ________u_u_uuu_uu_uuuu_uu_uuu_uuu_u_uuuuuu__u_uu_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Task.cpp -RES: _____________c_______ccccc_cccp_uuu_uuuuu_uuu_uuu_uuu_uuu +RES: _____________c_______ccccc_cccc_uuu_uuuuu_uuu_uuu_uuu_uuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Capture.h RES: _______________________c__cccc___uuuuuuuu__________________________ccuuu_u_c_____uuuu_____________uuuuuu_u_uu_uuuuu_u_ @@ -107,10 +107,10 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Limi RES: _____________u_____uu___u_________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.cpp -RES: __________c_ccu_uuuu_u_uuu___uu_u__uuu_uuuuu_u_uuu_u_c_c_u_u_uu_u_uu_uuuu_c_cuucuuuu_ccc_ +RES: __________c_ccc_cccc_c_ccc___cc_c__ccc_ccccc_c_ccc_c_c_c_c_c_cc_c_cc_cccc_c_cccccccc_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\GPIO.cpp -RES: _____________________________uu__uu__u_u__u_________pu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu_uu_uu_uu_uuuuuuu______________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu_p_p_p_pu_uu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uu_uuuuu_u_uuuu_u_uuuuu_pu_uuu_pu_uuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu______uu_uu_uu_uu_____________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu__p_p_p_ +RES: _____________________________cc__cc__c_c__c_________cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_ccccccc______________cccccc_ccc__ccccc_ccc_c__c_ccccc_cc_c_c_c_cc_cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_cc_ccccc_c_cccc_c_ccccc_cc_ccc_cc_ccc_cc_cc_cc_cuuu_u_uuuu_u_uuuuu______cc_cc_cc_cc_____________cccccc_cuc__ccccc_cuu_u__u_uuuuu_uu__c_c_c_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\LedcPin.cpp RES: ______________________u________uuuu_uu_u_u_u____uuuuuu_uuu____uuuu @@ -122,7 +122,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10v RES: ___________________________________u_uuuuu__u_u_________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-internal.h -RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________p____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________c____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StepStick.cpp RES: __________u_uuu_uu__u_uu_uuuuu_uuu__uu_u__u___c__ @@ -149,7 +149,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spi RES: ________________u_uuuu_uuuuu_u_uuuuu_u__uu_uuu_uuuu_________uuu_uuuu__uuu__uuuuu_uuuu_u_uuuuu_uuuuuuu_uu_uuu_uuuu_uu_u_uuu_uu_______uu___uuuuu___u_u_uu_u__u___u_u__uu_uuuu_u_uu___u_u__uu_uuu_uuuu____uu_uu_uuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.cpp -RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__cuuuuuu_uuuuuuuuu_uu_uc__cc_u_c_uuu_uuuuuuuu_c_____c__c___c__c_ccuu___cucu___cu__cc_cccc_cccc_cccu_cu_cu__cc_ +RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__ccuuuuu_cuuuuuccu_uu_cc__cc_u_c_ccu_cccccccc_c_____c__c___c__c_cccc___cucu___cc__cc_cccc_cccc_cccu_cu_cu__cc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.cpp RES: ____________uu_uu__u__uu_u_uu_ @@ -167,7 +167,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Motor RES: ____________________________uuu_u_u_uuuuuuuuuuuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.h -RES: ______________________________________u____u__uu__uc____________cc__ +RES: ______________________________________u____c__cc__cc____________cc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.cpp RES: _____________u_uuu_uu_uuuuu____uu__u_uu_uuuu_u_u_____________uu_u_uu__uuuu_uuuuu_uuu_u_uu_uu @@ -347,13 +347,13 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinD RES: _______________u___________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Main.cpp -RES: _____________________________________uuuu_uuu_uuu_u_uu_uuuuu_uuu_u___uuu___uuu___u_u____________________________________________u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ +RES: ______________________________uuu___u_u_u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.cpp -RES: _______cu_u_uu_uuuu_u__ +RES: _______cu_c_cc_ccuu_c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.h -RES: ________________________u__ +RES: ________________________c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Limits.cpp RES: __________________u________________u_____uuu_uuuuu_uu____uuu_uuuuuu_uu__uu____uu_uu_uuuuuuu_uu___uu_uuuu_u_uuuu_u_______________________uuuuu__uu_uuuuu__uu @@ -401,7 +401,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homi RES: ________________________________________uuuuu_uuu_u_uuu___u__u_uu_u_u___u_uuuu_uuuuuuu_______u_uuuu_u___uuuuu_u__uuuuuuuuu_uu_uuuu_uu_u__uu__u_uu__u__u_uu__u_____u_u_u_u_uuu_u__u_u__u_uu_uuu___uuu_u__uuuuu__uu_u__uu___uuu_u__uuuuu___uu_u_uu___________u________uu__uuu_uuuu_uuu_uu_uuuuu_uuuu_u_uuuuuu__u___uu_uuuuuu__uuuu_uuu_uu__u__uuu_u_u__u___________uuu_uuuu__u_uuuuuuuu_uuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.h -RES: __________________________________u____ +RES: __________________________________c____ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Commands.cpp RES: __________________________uuuu_uuu_uuuu___uu___uuuu_uuu____u____uu_uuu_u_ @@ -416,28 +416,28 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\Pwm RES: _________________u_____________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Arduino.cpp -RES: ________uuu_uu_uuuu__u_ccc_ccc_uuuu_cccc_uuuu_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ +RES: ________uuu_uu_uuuu__u_ccc_ccc_cccc_cccc_cccc_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.h RES: ________________________________uuu__u__u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SoftwareGPIO.h -RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_uu_u_cc_c_uu___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cuuuu__c_cc_cuuuuuu_uc_c_u_ccc_cccc_ccccc_ +RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_cc_c_cc_c_cc___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cccuu__c_cc_cccuuuu_cc_c_c_ccc_cccc_ccccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\PinOptionParsing.cpp -RES: _______puu__uuu___uuu_uuuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuuuuu_uuuuuu_uu___uuuuuuuu_uuu_ +RES: _______ccc__ccc___cuu_cuuc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_ccc_cc___ccccuuuu_ccc_cc_c_c__cccc_ccc_cc___ccccuuuu_ccc_cc_c_c__ccccccc_cccccc_cc___ccccuuuu_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\TestFactory.cpp RES: ______________________________________uu__uu____uu__uu_uuuu_u_uuu__u_u_uuu_u_uu_uu_uuuu_uu_uu_uu_uu__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\Undefined.cpp -RES: ______p__uu__uuu___uuu__uu_uuuu_p_uu_uu__uu_uu__uu_uu__uu_uuu_ +RES: ______c__cc__ccc___ccc__pc_cccc_c_cc_cc__cc_cc__cc_cc__cc_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\ErrorPinTest.cpp -RES: ______c__c_cc_c_cc_pu_uu_ +RES: ______c__c_cc_c_cc_pc_cc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Extender\I2CExtenderTests.cpp -RES: ________________u_____u_c_c_____c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__c_cccccc__ccc__cccccc_c_cccccc__ccc__ccccc_cccccccccccccc_____c_cccccccc__c_cccccc__ccc_c__ccccc___________________c_cc__c_cc_uuuu___uuuu_uuuu____uuuu_uuuu__uuuu_uuuu__uu____uuuu_uuuu________u_uu__u_uu_uuuu______u_uu__u_uu_uuuu___uuuu_uuuu___uuuu_uuuuu_cc__cccccc__ccc_c__ccccc___________________c_ccc__cc_cccc___cc_cc_cc____cccc_ccccc__cccc_ccccc__cc__c___cccc_ccc_cc________c_cc__c_cc_ccc_cc______c_cc__c_cc_ccc_cc___ccc____ccc____cccccccc___ccc____ccc_c_c__ccccccccccc_cucccccu_cu_c_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c__cc_c__c_cc_ccccc_c___ccc____cc__ccc_ccuuu__u__uuuuu_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c___ccc_c____cc____cc__c__cc_c__c__cc__cc_ +RES: _________________u_____u_c_c__________cc___ccc__cc_cc__ccc_ccc__cc_cc_u__c_ccccccc__cccc_cuccccccu_cu_c__cccccccc_cc_c_ccc_ccc_c_cc_ccc__cc___c_ccc_c_cu_c_c_cccccc______c_cccccccc__c___c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__cc_c__cccccc__ccc_cc__cccccc_ccc__cccccc__ccc_cc__ccccc_cccccccccccccc_ccc__cccccc__ccc_cc__ccccc____cc__c__cc____cccc_____ccccc___ccccc___cc_cc___cccc_ccc_____cc__c_ccc____cc__c_cccc____cc_cc____cc_cc__cc___cc_cc____cc_cc_c_cccc__cccccc__ccc_cc__ccccc__ccc_c____ccc_____cccc___cccc___cc__c___cccc______cc__cc____cc__cc____cccc____cccc____cccc____ccc____ccc_c_c_cccc__cccccc__ccc_cc__ccccc__cc__c_c__c__c__c_c__c___ccc____cc__cc__ccc___c__c_c____cc__cc_c_cccc__cccccc__ccc_cc__ccccc__cc__c_c__c___c____cc____c_c__c__cc___c_c__c__cc___c__c_c____cc__c_c_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.h RES: ______________c____c__c_____________ @@ -479,7 +479,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Notifi RES: ____________u__________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.cpp -RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccu_u_u_u________c__cc______cu__c_u__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccu_uuu_ccc_c +RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccc_u_c_c________c__cc______cu__c_c__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccc_ccc_ccc_c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.h RES: ________________________________u_______uu_______________u_uu_uuu_u__u_u__ @@ -533,7 +533,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.cpp RES: _____________________c___cuuuc_cc__uuuuu_uuu__u_u__uuu_u__uuuuuuuuu_uu_uuuuu_uuuu_uuuuu__uuuuuu_uuu_u__uu_u_uuuuu____uu_uuu_uuuuuuu_u__u_uuu_uuuuuu_u__uuuuuuuu_u_uuuuuu__uuuuu_uuuu_uuuu_uu_____uuuuuuuuu_c_uuuuu_uuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.cpp -RES: _____________________________________uuuuu_uuuuu_uuu_u_uuuu_____u_uu__uu__uuu_u_uuuu_u_uu__uuu_u__uuu_uuu_u_uuuu__uuu__uuu_uu_uu_uu__uu_uuu_uuu +RES: _____________________________________ccccc_ccccc_cuc_c_cccc_____u_cc__cc__uuu_u_uuuu_u_uu__ccc_c__ccu_ccc_c_ccuu__ccu__ccc_cc_cu_cu__cc_ccu_ccc PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.cpp RES: ___________u_u__u_uu_uuu__uu__uuu__uuuuuuuuu____u_u_uuuuuuuu__u_uuuuuuu__u_uuuuuu_u__u_uuuuuuu__u_uuuuuuuu_u_uuuuuuuuu_u_uuuu_u_uuuuu___u_uuuuuuuuuu_uu____uuu_uuuuuuuu_uuuuuuuuu_u_uuuuuuuuu_uu_u_uuuuuuu____u_uuu_ @@ -554,7 +554,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Hua RES: ______________uuuu__________u__u_____ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.cpp -RES: _________ccuuu_uu__ +RES: _________ccucc_uc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.cpp RES: _________________uuu__u_uuuu__u_u_uu_u_uu_uuu_uuuu_u_u___c__ @@ -647,7 +647,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\System.cpp RES: ____________________u_uuuuuuuuuu_uuuuuu_u_uuuuuuu_u_uuu_c__________c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.h -RES: ___________________________________________________cu___________________________________u_____________________ +RES: ___________________________________________________cc___________________________________u_____________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.cpp RES: __uuuuuuuu_c @@ -656,7 +656,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CB RES: _________ccccc_c_uuuuuu_ccc_c_cccu_c_cc_uu_u_u_u_u_u_u_u_u_u_u_uc___cccccc_c___ccc_cccc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.cpp -RES: _______________c_c_cc_____cu_uuuccc__ccc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c___c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_uu_uuu__uuuuuu_u_u__uuu_u_c_c__cc___ccc__cc___c_u +RES: _______________c_c_cc_____cu_uuuccu__uuc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c____c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_cc_ccc__cccccu_c_c__ccc_u_c_c__cc___ccc__cc___c_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.h RES: ______________________c___________________cc___c___c__ diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 9a378b36b..fa7b2bbf8 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -21,7 +21,7 @@ namespace Extenders { auto bus = _i2cBus; // First make sure the bus is empty, so we read that which we have written: - // while (bus->read(address, ®, 1) != 0) {} + while (bus->read(address, ®, 1) != 0) {} int err; if ((err = bus->write(address, ®, 1)) != 0) { @@ -42,6 +42,7 @@ namespace Extenders { return result; } } + void I2CExtender::I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { auto bus = _i2cBus; @@ -68,7 +69,7 @@ namespace Extenders { } // If an I/O error occurred, the best we can do is just reset the whole thing, and get it over with: - _status = 1; + _status |= 1; if (_outputReg != 0xFF) { _status |= 4; // writes } @@ -98,7 +99,7 @@ namespace Extenders { if (newStatus != 0) { if ((newStatus & 2) != 0) { _status = 0; - break; + return; // Stop running } // Update config: diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp index 80f5cba54..9212e84de 100644 --- a/FluidNC/test/Extender/I2CExtenderTests.cpp +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -10,6 +10,7 @@ #include "Capture.h" #include +#include namespace { struct GPIONative { @@ -27,6 +28,158 @@ namespace { inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } }; + + class PCA9539Emulator { + uint8_t reg_config[2]; + uint8_t reg_invert[2]; + uint8_t reg_input[2]; + uint8_t reg_output[2]; + uint8_t pad_value[2]; // input values + + uint8_t accessedRegisters = 0; + uint8_t previousRegisters = 0; // For debugging purposes. + + int isrPin_; + + void setRegister(int reg, uint8_t value) { + accessedRegisters |= uint8_t(1 << reg); + switch (reg) { + // input + case 2: + reg_output[0] = value; + break; + case 3: + reg_output[1] = value; + break; + // invert + case 4: + reg_invert[0] = value; + reg_input[0] = reg_invert[0] ^ pad_value[0]; + break; + case 5: + reg_invert[1] = value; + reg_input[1] = reg_invert[1] ^ pad_value[1]; + break; + // config + case 6: + reg_config[0] = value; + break; + case 7: + reg_config[1] = value; + break; + default: + Assert(false, "Not supported"); + break; + } + } + + void handler(TwoWire* theWire, std::vector& data) { + if (data.size() == 1) { + if (data[0] == 0 || data[0] == 1) { + accessedRegisters |= uint8_t(1 << data[0]); + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(reg_input[data[0]])); + data.clear(); + + // Clear ISR: + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, true); + } + } else if (data[0] >= 2 && data[0] <= 7) { + // ignore until next roundtrip + } else { + Assert(false, "Unknown register"); + } + } else if (data.size() == 2) { + if (data[0] >= 2 && data[0] <= 7) { + setRegister(data[0], data[1]); + data.clear(); + } else { + Assert(false, "Unknown register"); + } + } else { + Assert(false, "Unknown size"); + } + } + + public: + PCA9539Emulator(int isrPin) : isrPin_(isrPin) { + for (int i = 0; i < 2; ++i) { + reg_config[i] = 0; + reg_invert[i] = 0; + reg_input[i] = 0; + reg_output[i] = 0; + pad_value[i] = 0; + } + + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, true); + } + } + + static void wireResponseHandler(TwoWire* theWire, std::vector& data, void* userData) { + static_cast(userData)->handler(theWire, data); + } + + void setPadValue(int pinId, bool v) { + uint8_t mask = uint8_t(1 << (pinId % 8)); + int idx = pinId / 8; + + if (reg_config[idx] & mask) // input + { + auto oldValue = pad_value[idx] & mask; + auto newValue = v ? mask : uint8_t(0); + + if (oldValue != newValue) { + pad_value[idx] = (pad_value[idx] & ~mask) | newValue; + reg_input[idx] = reg_invert[idx] ^ pad_value[idx]; + + // Trigger ISR on 'falling'. + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, false); + } + } + } + } + + bool getPadValue(int pinId) { + uint8_t mask = uint8_t(1 << (pinId % 8)); + int idx = pinId / 8; + + if ((reg_config[idx] & mask) == 0) { + // This is an output pin, so combine registers: + return ((reg_output[idx] ^ reg_invert[idx]) & mask) != 0; + } else { + // This is an input pin, so use the pad_value + return (pad_value[idx] & mask) != 0; + } + } + + uint8_t registersUsed() { + auto result = accessedRegisters; + previousRegisters = result; + accessedRegisters = 0; + return result; + } + }; + + class Roundtrip { + uint32_t before; + + public: + Roundtrip() { before = Capture::instance().current(); } + + ~Roundtrip() { + for (int i = 0; i < 3; ++i) { + while (Capture::instance().current() < before + 1) { + delay(10); + } + before = Capture::instance().current(); + } + } + }; + + std::mutex single_thread; } namespace Configuration { @@ -104,6 +257,10 @@ namespace Configuration { }; Test(I2CExtender, InitDeinit) { + std::lock_guard guard(single_thread); + + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -117,6 +274,9 @@ namespace Configuration { mconfig._i2c = &bus; config = &mconfig; + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + // Setup the extender Extenders::I2CExtender i2c; FakeInitHandler fakeInit(false); @@ -126,6 +286,9 @@ namespace Configuration { } Test(I2CExtender, ClaimRelease) { + std::lock_guard guard(single_thread); + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -139,6 +302,9 @@ namespace Configuration { mconfig._i2c = &bus; config = &mconfig; + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + // Setup the extender Extenders::I2CExtender i2c; FakeInitHandler fakeInit(false); @@ -161,23 +327,10 @@ namespace Configuration { i2c.free(2); } - class Roundtrip { - uint32_t before; - - public: - Roundtrip() { before = Capture::instance().current(); } - - ~Roundtrip() { - for (int i = 0; i < 10; ++i) { - while (Capture::instance().current() < before + 1) { - delay(10); - } - before = Capture::instance().current(); - } - } - }; - Test(I2CExtender, ExtenderNoInterrupt) { + std::lock_guard guard(single_thread); + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -192,6 +345,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -200,25 +354,8 @@ namespace Configuration { i2c.validate(); i2c.init(); - // Expected register values (see datasheet): - // - // 4 invert - // 1 = invert, 0 = normal - // - // 6 config - // 1 = input, 0 = output - // - // 2 write - // 1 = high, 0 = low - // - // 0 read - // high = 1, low = 0 - { // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x01); i2c.claim(0); i2c.setupPin(0, Pin::Attr::Output); @@ -226,24 +363,17 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), - uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + // Check PCA values: + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(!pca.getPadValue(0)); } // Read will trigger an update, because we don't have an ISR { - Wire.Send(0x01); bool readPin = i2c.readPin(0); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 1, "Expected single data request / response roundtrip, got %d", int(recv.size())); - Assert(recv[0] == 0, "Expected read"); - Assert(readPin == true, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x01, "Expected roundtrip for read / no ISR"); + Assert(readPin == false, "Expected 'true' on pin"); } // Test write pin: @@ -252,28 +382,24 @@ namespace Configuration { i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(pca.getPadValue(0), "Expected pad to be 'true'"); } { // Write to set it 'low'. i2c.writePin(0, false); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 0, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(!pca.getPadValue(0), "Expected pad to be 'false'"); } { // Write to set it 'low'. It's already low, no-op. i2c.writePin(0, false); i2c.flushWrites(); // no-op. + Assert(pca.registersUsed() == 0x00, "Expected roundtrip for write / no ISR"); + Assert(!pca.getPadValue(0), "Expected pad to be 'false'"); } { // Write to set it 'high'. @@ -282,82 +408,83 @@ namespace Configuration { { Roundtrip rt; } auto recv = Wire.Receive(); - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(pca.getPadValue(0), "Expected pad to be 'false'"); } // NOTE: We ended with setting pin #0 to 'high' = 0x01 // Setup pin for reading: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - i2c.claim(1); i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), - uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(pca.getPadValue(0)); + Assert(!pca.getPadValue(1)); } // Setup another pin for reading with an invert mask and a PU: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x04); - i2c.claim(2); i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), - uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(pca.getPadValue(0)); + Assert(!pca.getPadValue(1)); + Assert(!pca.getPadValue(2)); } // Test read pin: { - Wire.Send(0x02); bool readPin = i2c.readPin(1); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); - Assert(recv[0] == 0, "Expected read"); + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); + Assert(readPin == false); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); Assert(readPin == true, "Expected 'true' on pin"); } + pca.setPadValue(1, true); + pca.setPadValue(2, true); + + // Test read pin: + { + bool readPin = i2c.readPin(1); + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); + Assert(readPin == true); + } + // Test read pin: { - Wire.Send(0x02); bool readPin = i2c.readPin(2); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); - Assert(recv[0] == 0, "Expected read"); + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); Assert(readPin == false, "Expected 'true' on pin"); } } Test(I2CExtender, ExtenderWithInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -373,6 +500,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender with ISR on gpio.15 Extenders::I2CExtender i2c; @@ -381,48 +509,19 @@ namespace Configuration { i2c.validate(); i2c.init(); - // Expected register values (see datasheet): - // - // 4 invert - // 1 = invert, 0 = normal - // - // 6 config - // 1 = input, 0 = output - // - // 2 write - // 1 = high, 0 = low - // - // 0 read - // high = 1, low = 0 - { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x01); - i2c.claim(0); i2c.setupPin(0, Pin::Attr::Output); { Roundtrip rt; } - // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), - uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); } // Read will NOT trigger an update because we have an ISR to tell us when it changes: { bool readPin = i2c.readPin(0); - auto recv = Wire.Receive(); - - Assert(recv.size() == 0, "Expected single data request / response roundtrip, got %d", int(recv.size())); - Assert(readPin == true, "Expected 'true' on pin"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'false' on pin"); + Assert(pca.registersUsed() == 0, "Expected no-op for read"); } // Test write pin: @@ -431,24 +530,14 @@ namespace Configuration { i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04, "Expected no-op for read"); } { // Write to set it 'low'. i2c.writePin(0, false); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 0, "Expected write reg 0 = 0"); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04); } { // Write to set it 'low'. It's already low, no-op. @@ -456,141 +545,83 @@ namespace Configuration { i2c.flushWrites(); // no-op. - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0, "Expected no-op"); } { // Write to set it 'high'. i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04); } // NOTE: We ended with setting pin #0 to 'high' = 0x01 // Setup pin for reading: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - i2c.claim(1); i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), - uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x55); } // Setup another pin for reading with an invert mask and a PU: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x04); - i2c.claim(2); i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), - uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x55); } // Test read pin: { bool readPin = i2c.readPin(1); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + { Roundtrip rt; } + Assert(pca.registersUsed() == 0x0); Assert(readPin == false, "Expected 'true' on pin"); } // Test read pin: { bool readPin = i2c.readPin(2); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + { Roundtrip rt; } + Assert(pca.registersUsed() == 0x0); Assert(readPin == true, "Expected 'true' on pin"); } // Trigger an ISR, change both pins { - Wire.Send(0x02); - GPIONative::write(15, true); - GPIONative::write(15, false); + pca.setPadValue(1, true); + pca.setPadValue(2, true); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1); - Assert(recv[0] == 0); + Assert(pca.registersUsed() == 0x01); } // Test read pin: { bool readPin = i2c.readPin(1); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x0); Assert(readPin == true, "Expected 'true' on pin"); } // Test read pin: { bool readPin = i2c.readPin(2); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x0); Assert(readPin == false, "Expected 'true' on pin"); } } void HandleInterrupt(void* data) { ++(*reinterpret_cast(data)); } - volatile uint16_t currentInput = 0; - void WireResponseHandler(TwoWire* theWire, std::vector& data) { - if (data.size() == 1) { - if (data[0] == 0) { - Assert(theWire->SendSize() == 0); - theWire->Send(uint8_t(currentInput)); - data.clear(); - } else if (data[0] == 1) { - Assert(theWire->SendSize() == 0); - theWire->Send(uint8_t(currentInput >> 8)); - data.clear(); - } else if (data[0] >= 2 && data[0] <= 7) { - // ignore until next roundtrip - } else { - Assert(false, "Unknown register"); - } - } else if (data.size() == 2) { - if (data[0] >= 2 && data[0] <= 7) { - data.clear(); - } else { - Assert(false, "Unknown register"); - } - } else { - Assert(false, "Unknown size"); - } - } - Test(I2CExtender, ISRTriggerWithInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -606,6 +637,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -615,45 +647,24 @@ namespace Configuration { i2c.init(); { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - Wire.Send(0x00); - i2c.claim(9); i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } uint32_t isrCounter = 0; { - Wire.Send(0x00); - Wire.Send(0x00); - i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } { Roundtrip rt; } @@ -661,38 +672,49 @@ namespace Configuration { // Test read pin: { bool readPin = i2c.readPin(9); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); Assert(readPin == false, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x00); } // Change state, wait till roundtrip { - Wire.Send(0x00); - Wire.Send(0x02); - - // Trigger ISR pin 'falling' - GPIONative::write(15, true); - GPIONative::write(15, false); - { Roundtrip rt; } - - auto recv = Wire.Receive(); - Assert(recv.size() == 2); - Assert(recv[0] == 0); - Assert(recv[1] == 1); + pca.setPadValue(9, true); { Roundtrip rt; } // Test if ISR update went correctly: Assert(isrCounter == 1); + Assert(pca.registersUsed() == 0x03); // Test read pin: bool readPin = i2c.readPin(9); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); Assert(readPin == true, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x00); + } + + { + i2c.detachInterrupt(9); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0xFF); + } + + // Change state, wait till roundtrip + { + pca.setPadValue(9, false); + { Roundtrip rt; } + + // Test if ISR detach went correctly: + Assert(isrCounter == 1); + Assert(pca.registersUsed() == 0x03); } } Test(I2CExtender, ISRTriggerWithoutInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -708,6 +730,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -717,35 +740,19 @@ namespace Configuration { i2c.init(); { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - Wire.Send(0x00); - i2c.claim(9); i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } uint32_t isrCounter = 0; { // From this point on, we just need to respond to wire requests - currentInput = 0x0000; - Wire.Clear(); - Wire.SetResponseHandler(WireResponseHandler); - i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); } @@ -757,7 +764,8 @@ namespace Configuration { // Change state, wait till roundtrip { - currentInput = 0x0200; + pca.setPadValue(9, true); + { Roundtrip rt; } // Test if ISR update went correctly: @@ -766,17 +774,37 @@ namespace Configuration { // Test read pin: bool readPin = i2c.readPin(9); Assert(readPin == true, "Expected 'true' on pin"); + } + + { + pca.setPadValue(9, false); { Roundtrip rt; } // Test if ISR update went correctly: - Assert(isrCounter == 1); + Assert(isrCounter == 2); // Test read pin: bool readPin2 = i2c.readPin(9); - Assert(readPin2 == true, "Expected 'true' on pin"); + Assert(readPin2 == false, "Expected 'false' on pin"); } - Wire.Clear(); + { + i2c.detachInterrupt(9); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0xFF); + } + + // Change state, wait till roundtrip + { + pca.setPadValue(9, false); + { Roundtrip rt; } + + // Test if ISR detach went correctly: + Assert(isrCounter == 2); + } } } diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 2ada77bbe..35ff5cdad 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -28,7 +28,8 @@ void TwoWire::Clear() { std::lock_guard guard(mut); sentData.clear(); receivedData.clear(); - handler = nullptr; + handler = nullptr; + handlerUserData = nullptr; } // TwoWire interface: @@ -127,7 +128,7 @@ size_t TwoWire::write(uint8_t ch) { } if (handler) { - (*handler)(this, sentData); + (*handler)(this, sentData, handlerUserData); } return 0; } diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index b28f807ca..866597748 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -14,7 +14,8 @@ class TwoWire : public Stream { std::vector sentData; std::mutex mut; - using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data); + using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data, void* userData); + void* handlerUserData; ResponseHandler handler; public: @@ -28,7 +29,10 @@ class TwoWire : public Stream { std::vector Receive(); size_t ReceiveSize() { return sentData.size(); } void Clear(); - void SetResponseHandler(ResponseHandler handler) { this->handler = handler; } + void SetResponseHandler(ResponseHandler handler, void* userData) { + this->handlerUserData = userData; + this->handler = handler; + } // TwoWire interface: From 683bdaa44316b483327fa84e9841d56bd6091a3a Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Mar 2022 15:42:31 +0100 Subject: [PATCH 076/100] Fixed a few bugs in the pin extender --- FluidNC/src/Extenders/I2CExtender.cpp | 45 +++++++++----- FluidNC/src/Machine/I2CBus.cpp | 9 ++- FluidNC/src/Machine/LimitPin.cpp | 4 +- FluidNC/src/Machine/Motor.cpp | 11 +++- FluidNC/src/Machine/Motor.h | 2 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 69 ++++++++++++++++++++-- 6 files changed, 110 insertions(+), 30 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index fa7b2bbf8..d0bde9d96 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -20,9 +20,6 @@ namespace Extenders { uint8_t I2CExtender::I2CGetValue(uint8_t address, uint8_t reg) { auto bus = _i2cBus; - // First make sure the bus is empty, so we read that which we have written: - while (bus->read(address, ®, 1) != 0) {} - int err; if ((err = bus->write(address, ®, 1)) != 0) { log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); @@ -37,6 +34,8 @@ namespace Extenders { IOError(); } else { + // This log line will probably generate a stack overflow and way too much data. Use with care: + // log_info("Request address: " << int(address) << ", reg: " << int(reg) << " gives: " << int(result)); errorCount = 0; } return result; @@ -56,6 +55,8 @@ namespace Extenders { log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); IOError(); } else { + // This log line will probably generate a stack overflow and way too much data. Use with care: + // log_info("Set address: " << int(address) << ", reg: " << int(reg) << " to: " << int(value)); errorCount = 0; } } @@ -188,12 +189,27 @@ namespace Extenders { // If we don't have an ISR, we must update everything. Otherwise, we can cherry pick: bool handleInvertSoftware = (_invertReg == 0xFF); + uint8_t newBytes[8]; for (int i = 0; i < claimedValues; ++i) { - auto oldByte = _input.bytes[i]; auto newByte = I2CGetValue(address, currentRegister); if (handleInvertSoftware) { newByte ^= _invert.bytes[i]; } + newBytes[i] = newByte; + + currentRegister++; + if (currentRegister == registersPerDevice + _inputReg) { + ++address; + } + } + + // Remove the busy flag, keep the rest. If we don't do that here, we + // end up with a race condition if we use _status in the ISR. + _status &= ~0x10; + + for (int i = 0; i < claimedValues; ++i) { + auto oldByte = _input.bytes[i]; + auto newByte = newBytes[i]; if (oldByte != newByte) { // Handle ISR's: @@ -206,16 +222,11 @@ namespace Extenders { auto o = (oldByte & mask); auto n = (newByte & mask); if (o != n) { - isr.callback(isr.data); + isr.callback(isr.data); // bug; race condition } } } } - - currentRegister++; - if (currentRegister == registersPerDevice + _inputReg) { - ++address; - } } } } @@ -288,11 +299,11 @@ namespace Extenders { // Ensure data is available: std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); - xTaskCreatePinnedToCore(isrTaskLoop, // task - "i2cHandler", // name for task - configMINIMAL_STACK_SIZE + 512, // size of task stack - this, // parameters - 1, // priority + xTaskCreatePinnedToCore(isrTaskLoop, // task + "i2cHandler", // name for task + configMINIMAL_STACK_SIZE + 512 + 2048, // size of task stack + this, // parameters + 1, // priority &_isrHandler, SUPPORT_TASK_CORE // core ); @@ -391,6 +402,8 @@ namespace Extenders { Assert(mode == CHANGE, "Only mode CHANGE is allowed for pin extender ISR's."); Assert(index < 64 && index >= 0, "Pin index out of range"); + // log_debug("Attaching interrupt (I2C) on index " << int(index)); + ISRData& data = _isrData[index]; data.callback = callback; data.data = arg; @@ -409,6 +422,8 @@ namespace Extenders { void I2CExtender::detachInterrupt(pinnum_t index) { Assert(index < 64 && index >= 0, "Pin index out of range"); + // log_debug("Detaching interrupt (I2C) on index " << int(index)); + ISRData& data = _isrData[index]; data.callback = nullptr; data.data = nullptr; diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index daf5120a2..e62b9d1de 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -77,13 +77,12 @@ namespace Machine { // log_debug("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " // << (i2c ? "non null" : "null")); - for (size_t i = 0; i < count; ++i) { - if (i2c->requestFrom((int)address, 1) != 1) { - return i; - } + size_t c = i2c->requestFrom((int)address, count); + + for (size_t i = 0; i < c; ++i) { data[i] = i2c->read(); } - return count; + return c; } } diff --git a/FluidNC/src/Machine/LimitPin.cpp b/FluidNC/src/Machine/LimitPin.cpp index 804b65baa..3f4f0dfb8 100644 --- a/FluidNC/src/Machine/LimitPin.cpp +++ b/FluidNC/src/Machine/LimitPin.cpp @@ -46,6 +46,7 @@ namespace Machine { void IRAM_ATTR LimitPin::handleISR() { read(); + if (sys.state != State::Alarm && sys.state != State::ConfigAlarm && sys.state != State::Homing) { if (_pHardLimits && rtAlarm == ExecAlarm::None) { #if 0 @@ -58,7 +59,7 @@ namespace Machine { } #endif - // log_debug("Hard limits"); // This might not work from ISR context + // log_debug("Hard limits"); // This might not work from ISR context mc_reset(); // Initiate system kill. rtAlarm = ExecAlarm::HardLimit; // Indicate hard limit critical event } @@ -88,6 +89,7 @@ namespace Machine { if (_pin.undefined()) { return; } + set_bitnum(Axes::limitMask, _axis); _pin.report(_legend.c_str()); auto attr = Pin::Attr::Input | Pin::Attr::ISR; diff --git a/FluidNC/src/Machine/Motor.cpp b/FluidNC/src/Machine/Motor.cpp index 18f475925..63b48282e 100644 --- a/FluidNC/src/Machine/Motor.cpp +++ b/FluidNC/src/Machine/Motor.cpp @@ -10,10 +10,9 @@ #include "Axes.h" namespace Machine { + Motor::Motor(int axis, int motorNum) : _axis(axis), _motorNum(motorNum) {} + void Motor::group(Configuration::HandlerBase& handler) { - _negLimitPin = new LimitPin(_negPin, _axis, _motorNum, -1, _hardLimits); - _posLimitPin = new LimitPin(_posPin, _axis, _motorNum, 1, _hardLimits); - _allLimitPin = new LimitPin(_allPin, _axis, _motorNum, 0, _hardLimits); handler.item("limit_neg_pin", _negPin); handler.item("limit_pos_pin", _posPin); handler.item("limit_all_pin", _allPin); @@ -29,6 +28,12 @@ namespace Machine { } void Motor::init() { + log_debug("Initializing motor / limits..."); + + _negLimitPin = new LimitPin(_negPin, _axis, _motorNum, -1, _hardLimits); + _posLimitPin = new LimitPin(_posPin, _axis, _motorNum, 1, _hardLimits); + _allLimitPin = new LimitPin(_allPin, _axis, _motorNum, 0, _hardLimits); + if (strcmp(_driver->name(), "null_motor") != 0) { set_bitnum(Machine::Axes::motorMask, _axis + 16 * _motorNum); } diff --git a/FluidNC/src/Machine/Motor.h b/FluidNC/src/Machine/Motor.h index 7fc1fed69..2a751d9d2 100644 --- a/FluidNC/src/Machine/Motor.h +++ b/FluidNC/src/Machine/Motor.h @@ -25,7 +25,7 @@ namespace Machine { int _motorNum; public: - Motor(int axis, int motorNum) : _axis(axis), _motorNum(motorNum) {} + Motor(int axis, int motorNum); MotorDrivers::MotorDriver* _driver = nullptr; float _pulloff = 1.0f; // mm diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp index 9212e84de..95e6f8dbb 100644 --- a/FluidNC/test/Extender/I2CExtenderTests.cpp +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -653,7 +653,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } uint32_t isrCounter = 0; @@ -664,7 +665,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } { Roundtrip rt; } @@ -697,7 +699,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } // Change state, wait till roundtrip @@ -746,7 +749,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } uint32_t isrCounter = 0; @@ -795,7 +799,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } // Change state, wait till roundtrip @@ -807,4 +812,58 @@ namespace Configuration { Assert(isrCounter == 2); } } + + void ReadInISRHandler(void* data) { + auto i2c = static_cast(data); + auto value = i2c->readPin(9); + Assert(value == true); + } + + Test(I2CExtender, ReadInISR) { + std::lock_guard guard(single_thread); + GPIONative::initialize(); + PCA9539Emulator pca(15); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); + } + + { + pca.setPadValue(9, false); + i2c.attachInterrupt(9, ReadInISRHandler, &i2c, CHANGE); + pca.setPadValue(9, true); + i2c.detachInterrupt(9); + pca.setPadValue(9, false); + } + } } From f63477251217260ede0b8e8db45a2979146be72e Mon Sep 17 00:00:00 2001 From: bdring Date: Sat, 16 Apr 2022 14:26:59 -0500 Subject: [PATCH 077/100] WIP --- FluidNC/src/Extenders/I2CExtender.cpp | 14 +++++++++++++- FluidNC/src/Extenders/I2CExtender.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index d0bde9d96..cc136c0e0 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -13,7 +13,9 @@ #include namespace Extenders { - EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, + { int(I2CExtenderDevice::PCA9555), "pca9555" }, + EnumItem(int(I2CExtenderDevice::Unknown)) }; I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} @@ -291,6 +293,16 @@ namespace Extenders { _operationReg = 6; break; + case I2CExtenderDevice::PCA9555: + // See data sheet page 7+: + _address = 0x20 + _deviceId; + _ports = 16; + _inputReg = 0; + _outputReg = 2; + _invertReg = 4; + _operationReg = 6; + break; + default: Assert(false, "Pin extender device is not supported!"); break; diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index d5bacc1b9..74ebb2f75 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -13,12 +13,14 @@ namespace Pins { class PCA9539PinDetail; + class PCA9555PinDetail; } namespace Extenders { enum class I2CExtenderDevice { Unknown, PCA9539, + PCA9555, }; // Pin extenders... From dec4c34a150fd1ec3dd6780865c61637bd666ecb Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 11:15:33 +0100 Subject: [PATCH 078/100] Added pin extender support. Made everything compile yet again. --- FluidNC/src/Extenders/Extenders.cpp | 58 ++++ FluidNC/src/Extenders/Extenders.h | 25 ++ FluidNC/src/Extenders/PCA9539.cpp | 271 ++++++++++++++++++ FluidNC/src/Extenders/PCA9539.h | 126 ++++++++ FluidNC/src/Extenders/PinExtender.h | 23 ++ FluidNC/src/Extenders/PinExtenderDriver.cpp | 12 + FluidNC/src/Extenders/PinExtenderDriver.h | 33 +++ FluidNC/src/Machine/I2CBus.cpp | 54 ++++ FluidNC/src/Machine/I2CBus.h | 33 +++ FluidNC/src/Machine/MachineConfig.cpp | 2 + FluidNC/src/Machine/MachineConfig.h | 4 + FluidNC/src/Main.cpp | 8 +- FluidNC/src/MotionControl.cpp | 4 +- FluidNC/src/Pins/ExtPinDetail.cpp | 93 ++++++ FluidNC/src/Pins/ExtPinDetail.h | 43 +++ FluidNC/test/Pins/GPIO.cpp | 2 +- UnitTests.vcxproj | 19 +- UnitTests.vcxproj.filters | 60 +++- X86TestSupport/TestSupport/Wire.cpp | 4 + X86TestSupport/TestSupport/Wire.h | 56 ++++ X86TestSupport/TestSupport/driver/uart.h | 2 + .../TestSupport/freertos/FreeRTOS.h | 2 + 22 files changed, 916 insertions(+), 18 deletions(-) create mode 100644 FluidNC/src/Extenders/Extenders.cpp create mode 100644 FluidNC/src/Extenders/Extenders.h create mode 100644 FluidNC/src/Extenders/PCA9539.cpp create mode 100644 FluidNC/src/Extenders/PCA9539.h create mode 100644 FluidNC/src/Extenders/PinExtender.h create mode 100644 FluidNC/src/Extenders/PinExtenderDriver.cpp create mode 100644 FluidNC/src/Extenders/PinExtenderDriver.h create mode 100644 FluidNC/src/Machine/I2CBus.cpp create mode 100644 FluidNC/src/Machine/I2CBus.h create mode 100644 FluidNC/src/Pins/ExtPinDetail.cpp create mode 100644 FluidNC/src/Pins/ExtPinDetail.h create mode 100644 X86TestSupport/TestSupport/Wire.cpp create mode 100644 X86TestSupport/TestSupport/Wire.h diff --git a/FluidNC/src/Extenders/Extenders.cpp b/FluidNC/src/Extenders/Extenders.cpp new file mode 100644 index 000000000..d2b46821f --- /dev/null +++ b/FluidNC/src/Extenders/Extenders.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" + +namespace Extenders { + PinExtender::PinExtender() : _driver(nullptr) {} + + void PinExtender::validate() const { + if (_driver) { + _driver->validate(); + } + } + void PinExtender::group(Configuration::HandlerBase& handler) { PinExtenderFactory::factory(handler, _driver); } + void PinExtender::init() { + if (_driver) { + _driver->init(); + } + } + + PinExtender::~PinExtender() { delete _driver; } + + Extenders::Extenders() { + for (int i = 0; i < 16; ++i) { + _pinDrivers[i] = nullptr; + } + } + + void Extenders::validate() const {} + + void Extenders::group(Configuration::HandlerBase& handler) { + for (int i = 0; i < 10; ++i) { + char tmp[11 + 3]; + tmp[0] = 0; + strcat(tmp, "pinextender"); + + for (size_t i = 0; i < 10; ++i) { + tmp[11] = char(i + '0'); + tmp[12] = '\0'; + handler.section(tmp, _pinDrivers[i]); + } + } + } + + void Extenders::init() { + for (int i = 0; i < 16; ++i) { + if (_pinDrivers[i] != nullptr) { + _pinDrivers[i]->init(); + } + } + } + + Extenders::~Extenders() { + for (int i = 0; i < 16; ++i) { + delete _pinDrivers[i]; + } + } +} diff --git a/FluidNC/src/Extenders/Extenders.h b/FluidNC/src/Extenders/Extenders.h new file mode 100644 index 000000000..3d83af476 --- /dev/null +++ b/FluidNC/src/Extenders/Extenders.h @@ -0,0 +1,25 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "../Configuration/GenericFactory.h" +#include "PinExtender.h" + +namespace Extenders { + class Extenders : public Configuration::Configurable { + public: + Extenders(); + + PinExtender* _pinDrivers[16]; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + void init(); + + ~Extenders(); + }; + + using PinExtenderFactory = Configuration::GenericFactory; +} diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp new file mode 100644 index 000000000..d7f40f2a1 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" +#include "PCA9539.h" +#include "../Logging.h" + +#include +#include + +namespace Extenders { + void PCA9539::claim(pinnum_t index) { + Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); + + _claimed |= mask; + } + + void PCA9539::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed &= ~mask; + } + + uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + auto err = bus->write(address, ®, 1); + + if (err) { + // log_info("Error writing to i2c bus. Code: " << err); + return 0; + } + + uint8_t inputData; + if (bus->read(address, &inputData, 1) != 1) { + // log_info("Error reading from i2c bus."); + } + + return inputData; + } + + void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + auto err = bus->write(address, data, 2); + + if (err) { + log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); + } + } + + void PCA9539::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); + } + + void PCA9539::group(Configuration::HandlerBase& handler) { + handler.item("interrupt0", _isrData[0]._pin); + handler.item("interrupt1", _isrData[1]._pin); + handler.item("interrupt2", _isrData[2]._pin); + handler.item("interrupt3", _isrData[3]._pin); + } + + void PCA9539::isrTaskLoop(void* arg) { + auto inst = static_cast(arg); + while (true) { + void* ptr; + if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { + ISRData* valuePtr = static_cast(ptr); + // log_info("PCA state change ISR"); + valuePtr->updateValueFromDevice(); + } + } + } + + void PCA9539::init() { + this->_i2cBus = config->_i2c; + + _isrQueue = xQueueCreate(16, sizeof(void*)); + xTaskCreatePinnedToCore(isrTaskLoop, // task + "isr_handler", // name for task + configMINIMAL_STACK_SIZE + 256, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + data._address = uint8_t(0x74 + i); + data._container = this; + data._valueBase = reinterpret_cast(&_value) + i; + + // Update the value first by reading it: + data.updateValueFromDevice(); + + if (!data._pin.undefined()) { + data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + + // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. + data._pin.attachInterrupt(updatePCAState, FALLING, &data); + } else { + // Reset valueBase so we know it's not bound to an ISR: + data._valueBase = nullptr; + } + } + } + + void PCA9539::ISRData::updateValueFromDevice() { + const uint8_t InputReg = 0; + auto i2cBus = _container->_i2cBus; + + auto r1 = I2CGetValue(i2cBus, _address, InputReg); + auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); + uint16_t oldValue = *_valueBase; + uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); + *_valueBase = value; + + if (_hasISR) { + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1) << i; + + if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { + // log_info("State change pin " << i); + switch (_isrMode[i]) { + case RISING: + if ((value & mask) == mask) { + _isrCallback[i](_isrArgument); + } + break; + case FALLING: + if ((value & mask) == 0) { + _isrCallback[i](_isrArgument); + } + break; + case CHANGE: + _isrCallback[i](_isrArgument); + break; + } + } + } + } + } + + void PCA9539::updatePCAState(void* ptr) { + ISRData* valuePtr = static_cast(ptr); + + BaseType_t xHigherPriorityTaskWoken = false; + xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); + } + + void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { + bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); + bool output = attr.has(Pins::PinAttributes::Output); + + uint64_t mask = uint64_t(1) << index; + _invert = (_invert & ~mask) | (activeLow ? mask : 0); + _configuration = (_configuration & ~mask) | (output ? 0 : mask); + + const uint8_t deviceId = index / 16; + + const uint8_t ConfigReg = 6; + uint8_t address = 0x74 + deviceId; + + uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); + uint8_t reg = ConfigReg + ((index / 8) & 1); + + // log_info("Setup reg " << int(reg) << " with value " << int(value)); + + I2CSetValue(_i2cBus, address, reg, value); + } + + void PCA9539::writePin(pinnum_t index, bool high) { + uint64_t mask = uint64_t(1) << index; + uint64_t oldVal = _value; + uint64_t newVal = high ? mask : uint64_t(0); + _value = (_value & ~mask) | newVal; + + _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); + } + + bool PCA9539::readPin(pinnum_t index) { + uint8_t reg = uint8_t(index / 8); + uint8_t deviceId = reg / 2; + + // If it's handled by the ISR, we don't need to read anything from the device. + // Otherwise, we do. Check: + if (_isrData[deviceId]._valueBase == nullptr) { + const uint8_t InputReg = 0; + uint8_t address = 0x74 + deviceId; + + auto readReg = InputReg + (reg & 1); + auto value = I2CGetValue(_i2cBus, address, readReg); + uint64_t newValue = uint64_t(value) << (int(reg) * 8); + uint64_t mask = uint64_t(0xff) << (int(reg) * 8); + + _value = ((newValue ^ _invert) & mask) | (_value & ~mask); + + // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } + // else { + // log_info("No read, value is " << int(_value)); + // } + + return (_value & (1ull << index)) != 0; + } + + void PCA9539::flushWrites() { + uint64_t write = _value ^ _invert; + for (int i = 0; i < 8; ++i) { + if ((_dirtyRegisters & (1 << i)) != 0) { + const uint8_t OutputReg = 2; + uint8_t address = 0x74 + (i / 2); + + uint8_t val = uint8_t(write >> (8 * i)); + uint8_t reg = OutputReg + (i & 1); + I2CSetValue(_i2cBus, address, reg, val); + } + } + + _dirtyRegisters = 0; + } + + // ISR's: + void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + int device = index / 16; + int pinNumber = index % 16; + + Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); + + _isrData[device]._isrCallback[pinNumber] = callback; + _isrData[device]._isrArgument[pinNumber] = arg; + _isrData[device]._isrMode[pinNumber] = mode; + _isrData[device]._hasISR = true; + } + + void PCA9539::detachInterrupt(pinnum_t index) { + int device = index / 16; + int pinNumber = index % 16; + + _isrData[device]._isrCallback[pinNumber] = nullptr; + _isrData[device]._isrArgument[pinNumber] = nullptr; + _isrData[device]._isrMode[pinNumber] = 0; + + bool hasISR = false; + for (int i = 0; i < 16; ++i) { + hasISR |= (_isrData[device]._isrArgument[i] != nullptr); + } + _isrData[device]._hasISR = hasISR; + } + + const char* PCA9539::name() const { return "pca9539"; } + + PCA9539 ::~PCA9539() { + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + if (!data._pin.undefined()) { + data._pin.detachInterrupt(); + } + } + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("pca9539"); + } +} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h new file mode 100644 index 000000000..d90068370 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.h @@ -0,0 +1,126 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + class PCA9539 : public PinExtenderDriver { + friend class Pins::PCA9539PinDetail; + + // Address can be set for up to 4 devices. Each device supports 16 pins. + + static const int numberPins = 16 * 4; + uint64_t _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Registers: + // 4x16 = 64 bits. Fits perfectly into an uint64. + uint64_t _configuration = 0; + uint64_t _invert = 0; + volatile uint64_t _value = 0; + + // 4 devices, 2 registers per device. 8 bits is enough: + uint8_t _dirtyRegisters = 0; + + QueueHandle_t _isrQueue = nullptr; + TaskHandle_t _isrHandler = nullptr; + + static void isrTaskLoop(void* arg); + + struct ISRData { + ISRData() = default; + + Pin _pin; + PCA9539* _container = nullptr; + volatile uint16_t* _valueBase = nullptr; + uint8_t _address = 0; + + typedef void (*ISRCallback)(void*); + + bool _hasISR = false; + ISRCallback _isrCallback[16] = { 0 }; + void* _isrArgument[16] = { 0 }; + int _isrMode[16] = { 0 }; + + void IRAM_ATTR updateValueFromDevice(); + }; + + ISRData _isrData[4]; + static void IRAM_ATTR updatePCAState(void* ptr); + + public: + PCA9539() = default; + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~PCA9539(); + }; +} diff --git a/FluidNC/src/Extenders/PinExtender.h b/FluidNC/src/Extenders/PinExtender.h new file mode 100644 index 000000000..2c60327c1 --- /dev/null +++ b/FluidNC/src/Extenders/PinExtender.h @@ -0,0 +1,23 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "PinExtenderDriver.h" + +namespace Extenders { + class PinExtender : public Configuration::Configurable { + public: + // Other configurations? + PinExtenderDriver* _driver; + + PinExtender(); + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + void init(); + + ~PinExtender(); + }; +} diff --git a/FluidNC/src/Extenders/PinExtenderDriver.cpp b/FluidNC/src/Extenders/PinExtenderDriver.cpp new file mode 100644 index 000000000..efc602acc --- /dev/null +++ b/FluidNC/src/Extenders/PinExtenderDriver.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "PinExtenderDriver.h" + +namespace Extenders { + + void PinExtenderDriver::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + Assert(false, "Interrupts are not supported by pin extender for pin %d", index); + } + void PinExtenderDriver::detachInterrupt(pinnum_t index) { Assert(false, "Interrupts are not supported by pin extender"); } +} diff --git a/FluidNC/src/Extenders/PinExtenderDriver.h b/FluidNC/src/Extenders/PinExtenderDriver.h new file mode 100644 index 000000000..dfc4e79ad --- /dev/null +++ b/FluidNC/src/Extenders/PinExtenderDriver.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" +#include "../Pins/PinAttributes.h" + +#include "../Platform.h" + +namespace Extenders { + class PinExtenderDriver : public Configuration::Configurable { + public: + virtual void init() = 0; + + virtual void claim(pinnum_t index) = 0; + virtual void free(pinnum_t index) = 0; + + virtual void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) = 0; + virtual void IRAM_ATTR writePin(pinnum_t index, bool high) = 0; + virtual bool IRAM_ATTR readPin(pinnum_t index) = 0; + virtual void IRAM_ATTR flushWrites() = 0; + + virtual void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode); + virtual void detachInterrupt(pinnum_t index); + + // Name is required for the configuration factory to work. + virtual const char* name() const = 0; + + // Virtual base classes require a virtual destructor. + virtual ~PinExtenderDriver() {} + }; +} diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp new file mode 100644 index 000000000..efc6dded3 --- /dev/null +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "I2CBus.h" + +namespace Machine { + void I2CBus::validate() const { + if (_sda.defined() || _scl.defined()) { + Assert(_sda.defined(), "I2C SDA pin should be configured once"); + Assert(_scl.defined(), "I2C SCL pin should be configured once"); + Assert(_busNumber == 0 || _busNumber == 1, "The ESP32 only has 2 I2C buses. Number %d is invalid", _busNumber); + } + } + + void I2CBus::group(Configuration::HandlerBase& handler) { + handler.item("sda", _sda); + handler.item("scl", _scl); + handler.item("busNumber", _busNumber); + handler.item("frequency", _frequency); + } + + void I2CBus::init() { + log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); + + auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + + if (_busNumber == 0) { + i2c = &Wire; + } else { + i2c = &Wire1; + } + i2c->begin(sdaPin, sclPin /*, _frequency */); + } + + int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { + i2c->beginTransmission(address); + for (size_t i = 0; i < count; ++i) { + i2c->write(data[i]); + } + return i2c->endTransmission(); // i2c_err_t ?? + } + + int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { + for (size_t i = 0; i < count; ++i) { + if (i2c->requestFrom((int)address, 1) != 1) { + return i; + } + data[i] = i2c->read(); + } + return count; + } + +} diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h new file mode 100644 index 000000000..a55673ae5 --- /dev/null +++ b/FluidNC/src/Machine/I2CBus.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "../Configuration/Configurable.h" + +#include +#include + +namespace Machine { + class I2CBus : public Configuration::Configurable { + protected: + TwoWire* i2c; + + public: + I2CBus() = default; + + int _busNumber = 0; + Pin _sda; + Pin _scl; + uint32_t _frequency = 0; + + void init(); + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! + int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); + + ~I2CBus() = default; + }; +} diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index 81707ec50..8529c3450 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -40,12 +40,14 @@ namespace Machine { handler.section("axes", _axes); handler.section("kinematics", _kinematics); handler.section("i2so", _i2so); + handler.section("i2c", _i2c); handler.section("spi", _spi); handler.section("sdcard", _sdCard); handler.section("control", _control); handler.section("coolant", _coolant); handler.section("probe", _probe); handler.section("macros", _macros); + handler.section("extenders", _extenders); handler.section("start", _start); handler.section("user_outputs", _userOutputs); diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index 49e0819db..981ab8a71 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -11,6 +11,7 @@ #include "../CoolantControl.h" #include "../Kinematics/Kinematics.h" #include "../WebUI/BTConfig.h" +#include "../Extenders/Extenders.h" #include "../Control.h" #include "../Probe.h" #include "../SDCard.h" @@ -21,6 +22,7 @@ #include "../Config.h" #include "Axes.h" #include "SPIBus.h" +#include "I2CBus.h" #include "I2SOBus.h" #include "UserOutputs.h" #include "Macros.h" @@ -58,6 +60,7 @@ namespace Machine { Axes* _axes = nullptr; Kinematics* _kinematics = nullptr; SPIBus* _spi = nullptr; + I2CBus* _i2c = nullptr; I2SOBus* _i2so = nullptr; Stepping* _stepping = nullptr; CoolantControl* _coolant = nullptr; @@ -68,6 +71,7 @@ namespace Machine { Macros* _macros = nullptr; Start* _start = nullptr; Spindles::SpindleList _spindles; + Extenders::Extenders* _extenders = nullptr; float _arcTolerance = 0.002f; float _junctionDeviation = 0.01f; diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index f3c443031..043f0c205 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -72,17 +72,19 @@ void setup() { config->_sdCard->init(); } } + if (config->_i2c) { + config->_i2c->init(); + } + + // TODO FIXME: Initialize extenders *here* config->_stepping->init(); // Configure stepper interrupt timers plan_init(); config->_userOutputs->init(); - config->_axes->init(); - config->_control->init(); - config->_kinematics->init(); auto n_axis = config->_axes->_numberAxis; diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index 45ff79c25..839be53f6 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -133,7 +133,7 @@ void mc_arc(float* target, auto n_axis = config->_axes->_numberAxis; - float previous_position[n_axis] = { 0.0 }; + float previous_position[MAX_N_AXIS] = { 0.0f }; for (size_t i = 0; i < n_axis; i++) { previous_position[i] = position[i]; } @@ -165,7 +165,7 @@ void mc_arc(float* target, pl_data->motion.inverseTime = 0; // Force as feed absolute mode over arc segments. } float theta_per_segment = angular_travel / segments; - float linear_per_segment[n_axis]; + float linear_per_segment[MAX_N_AXIS]; linear_per_segment[axis_linear] = (target[axis_linear] - position[axis_linear]) / segments; for (size_t i = A_AXIS; i < n_axis; i++) { linear_per_segment[i] = (target[i] - position[i]) / segments; diff --git a/FluidNC/src/Pins/ExtPinDetail.cpp b/FluidNC/src/Pins/ExtPinDetail.cpp new file mode 100644 index 000000000..a5809f1b0 --- /dev/null +++ b/FluidNC/src/Pins/ExtPinDetail.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "ExtPinDetail.h" + +namespace Pins { + ExtPinDetail::ExtPinDetail(int device, pinnum_t index, const PinOptionsParser& options) : + PinDetail(index), _device(device), _capabilities(PinCapabilities::Output | PinCapabilities::Input | PinCapabilities::ISR), + _attributes(Pins::PinAttributes::Undefined) { + // User defined pin capabilities + for (auto opt : options) { + if (opt.is("low")) { + _attributes = _attributes | PinAttributes::ActiveLow; + } else if (opt.is("high")) { + // Default: Active HIGH. + } else { + Assert(false, "Unsupported I2SO option '%s'", opt()); + } + } + } + + PinCapabilities ExtPinDetail::capabilities() const { return PinCapabilities::Input | PinCapabilities::Output | PinCapabilities::ISR; } + + // I/O: + void ExtPinDetail::write(int high) { + Assert(_owner != nullptr, "Cannot write to uninitialized pin"); + _owner->writePin(_index, high); + } + + void ExtPinDetail::synchronousWrite(int high) { + Assert(_owner != nullptr, "Cannot write to uninitialized pin"); + _owner->writePin(_index, high); + _owner->flushWrites(); + } + + int ExtPinDetail::read() { + Assert(_owner != nullptr, "Cannot read from uninitialized pin"); + return _owner->readPin(_index); + } + + void ExtPinDetail::setAttr(PinAttributes value) { + // We setup the driver in setAttr. Before this time, the owner might not be valid. + + // Check the attributes first: + Assert(value.has(PinAttributes::Input) || value.has(PinAttributes::Output), "PCA9539 pins can be used as either input or output."); + Assert(value.has(PinAttributes::Input) != value.has(PinAttributes::Output), "PCA9539 pins can be used as either input or output."); + Assert(value.validateWith(this->_capabilities), "Requested attributes do not match the PCA9539 pin capabilities."); + Assert(!_attributes.conflictsWith(value), "Attributes on this pin have been set before, and there's a conflict."); + + _attributes = value; + + bool activeLow = _attributes.has(PinAttributes::ActiveLow); + + if (_owner == nullptr) { + auto ext = config->_extenders; + if (ext != nullptr && ext->_pinDrivers[_device] != nullptr && ext->_pinDrivers[_device]->_driver != nullptr) { + _owner = ext->_pinDrivers[_device]->_driver; + } else { + Assert(false, "Cannot find pin extender definition in configuration for pin pinext%d.%d", _device, _index); + } + + _owner->claim(_index); + } + + _owner->setupPin(_index, _attributes); + _owner->writePin(_index, value.has(PinAttributes::InitialOn)); + } + + PinAttributes ExtPinDetail::getAttr() const { return _attributes; } + + void ExtPinDetail::attachInterrupt(void (*callback)(void*), void* arg, int mode) { + Assert(_owner != nullptr, "Cannot attach ISR on uninitialized pin"); + _owner->attachInterrupt(_index, callback, arg, mode); + } + void ExtPinDetail::detachInterrupt() { + Assert(_owner != nullptr, "Cannot detach ISR on uninitialized pin"); + _owner->detachInterrupt(_index); + } + + String ExtPinDetail::toString() { + auto s = String("pinext") + int(_device) + String(".") + int(_index); + if (_attributes.has(PinAttributes::ActiveLow)) { + s += ":low"; + } + return s; + } + + ExtPinDetail::~ExtPinDetail() { + if (_owner) { + _owner->free(_index); + } + } +} diff --git a/FluidNC/src/Pins/ExtPinDetail.h b/FluidNC/src/Pins/ExtPinDetail.h new file mode 100644 index 000000000..df1f9320f --- /dev/null +++ b/FluidNC/src/Pins/ExtPinDetail.h @@ -0,0 +1,43 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinDetail.h" +#include "../Extenders/PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" + +#include + +namespace Pins { + class ExtPinDetail : public PinDetail { + Extenders::PinExtenderDriver* _owner = nullptr; + int _device; + + PinCapabilities _capabilities; + PinAttributes _attributes; + + public: + ExtPinDetail(int device, pinnum_t index, const PinOptionsParser& options); + + PinCapabilities capabilities() const override; + + // I/O: + void write(int high) override; + void synchronousWrite(int high) override; + int read() override; + + // ISR's: + void attachInterrupt(void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt() override; + + void setAttr(PinAttributes value) override; + PinAttributes getAttr() const override; + + String toString() override; + + ~ExtPinDetail() override; + }; +} diff --git a/FluidNC/test/Pins/GPIO.cpp b/FluidNC/test/Pins/GPIO.cpp index c17acdde3..39ec3d49d 100644 --- a/FluidNC/test/Pins/GPIO.cpp +++ b/FluidNC/test/Pins/GPIO.cpp @@ -301,7 +301,7 @@ namespace Pins { hitCount = 0; int expected = 0; - gpio16.attachInterrupt(this, mode); + // gpio16.attachInterrupt(this, mode); // Two ways to set I/O: // 1. using on/off diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 1f9d64333..93d739c8d 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -33,8 +33,15 @@ + + + + + + + @@ -97,7 +104,6 @@ - @@ -206,10 +212,17 @@ + + + + + + + @@ -223,6 +236,7 @@ + @@ -240,7 +254,6 @@ - @@ -305,7 +318,6 @@ - @@ -347,6 +359,7 @@ + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 34ac5140a..d8f5ac631 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -46,6 +46,9 @@ {dc4ba3bc-4342-4a67-aea2-f68877f1ee69} + + {2b9b9202-4bb3-450e-a90b-99f042de0af2} + @@ -225,9 +228,6 @@ src\Configuration - - src - src\Motors @@ -573,6 +573,30 @@ X86TestSupport + + X86TestSupport + + + src + + + src\Machine + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + src\Pins + @@ -665,9 +689,6 @@ src\Configuration - - src - src\StackTrace @@ -815,9 +836,6 @@ src\Spindles - - src - src\Machine @@ -989,6 +1007,30 @@ X86TestSupport + + src + + + src + + + src\Machine + + + src\Extenders + + + src\Extenders + + + src\Extenders + + + X86TestSupport + + + src\Pins + diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp new file mode 100644 index 000000000..046c93eda --- /dev/null +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -0,0 +1,4 @@ +#include "Wire.h" + +TwoWire Wire(0); +TwoWire Wire1(1); diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h new file mode 100644 index 000000000..aa9f01dcf --- /dev/null +++ b/X86TestSupport/TestSupport/Wire.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include "Stream.h" + +class TwoWire : public Stream { +public: + TwoWire(uint8_t bus_num) {} + ~TwoWire() {} + + //call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl) {} + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } + bool end() { return true; } + + void setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms + uint16_t getTimeOut() { return 0; } + + bool setClock(uint32_t) {} + uint32_t getClock() { return 0; } + + void beginTransmission(uint16_t address) {} + void beginTransmission(uint8_t address) {} + void beginTransmission(int address) {} + + uint8_t endTransmission(bool sendStop) { return 0; } + uint8_t endTransmission(void) { return 0; } + + size_t requestFrom(uint16_t address, size_t size, bool sendStop) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { return 0; } + size_t requestFrom(uint8_t address, size_t len, bool stopBit) { return 0; } + uint8_t requestFrom(uint16_t address, uint8_t size) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t size) { return 0; } + uint8_t requestFrom(int address, int size, int sendStop) { return 0; } + uint8_t requestFrom(int address, int size) { return 0; } + + size_t write(uint8_t) { return 0; } + size_t write(const uint8_t*, size_t) { return 0; } + int available(void) { return 0; } + int read(void) { return 0; } + int peek(void) { return 0; } + void flush(void) {} + + inline size_t write(const char* s) { return write((uint8_t*)s, strlen(s)); } + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } +}; + +extern TwoWire Wire; +extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/driver/uart.h b/X86TestSupport/TestSupport/driver/uart.h index 9b7192685..8efa6add0 100644 --- a/X86TestSupport/TestSupport/driver/uart.h +++ b/X86TestSupport/TestSupport/driver/uart.h @@ -77,6 +77,8 @@ typedef struct { bool use_ref_tick; /*!< Set to true if UART should be clocked from REF_TICK */ } uart_config_t; +const int UART_FIFO_LEN = 128; + esp_err_t uart_flush(uart_port_t uart_num); esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t* uart_config); esp_err_t uart_driver_install( diff --git a/X86TestSupport/TestSupport/freertos/FreeRTOS.h b/X86TestSupport/TestSupport/freertos/FreeRTOS.h index 6b20e1612..11df765a5 100644 --- a/X86TestSupport/TestSupport/freertos/FreeRTOS.h +++ b/X86TestSupport/TestSupport/freertos/FreeRTOS.h @@ -29,3 +29,5 @@ inline void vTaskEnterCritical(portMUX_TYPE* mux) { inline int32_t xPortGetFreeHeapSize() { return 1024 * 1024 * 4; } + +#define configMINIMAL_STACK_SIZE 768 From 255eb39f82cca79734fce609fa7e32c10d56ded2 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 17:03:09 +0100 Subject: [PATCH 079/100] Added some test code to first check if the pin extender works. Seems to be fine. --- FluidNC/src/Extenders/PCA9539.cpp | 23 ++++---- FluidNC/src/Machine/I2CBus.cpp | 12 +++- FluidNC/src/Machine/I2CBus.h | 19 ++++++- FluidNC/src/Main.cpp | 94 ++++++++++++++++++++++++++++++- FluidNC/src/Pin.cpp | 11 ++++ 5 files changed, 143 insertions(+), 16 deletions(-) diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp index d7f40f2a1..eea3cb056 100644 --- a/FluidNC/src/Extenders/PCA9539.cpp +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -24,22 +24,26 @@ namespace Extenders { } uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); + auto err = bus->write(address, ®, 1); if (err) { - // log_info("Error writing to i2c bus. Code: " << err); + log_info("Error writing to i2c bus. Code: " << err); return 0; } uint8_t inputData; if (bus->read(address, &inputData, 1) != 1) { - // log_info("Error reading from i2c bus."); + log_info("Error reading from i2c bus."); } return inputData; } void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); + uint8_t data[2]; data[0] = reg; data[1] = uint8_t(value); @@ -68,7 +72,7 @@ namespace Extenders { void* ptr; if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { ISRData* valuePtr = static_cast(ptr); - // log_info("PCA state change ISR"); + log_info("PCA state change ISR"); valuePtr->updateValueFromDevice(); } } @@ -80,7 +84,7 @@ namespace Extenders { _isrQueue = xQueueCreate(16, sizeof(void*)); xTaskCreatePinnedToCore(isrTaskLoop, // task "isr_handler", // name for task - configMINIMAL_STACK_SIZE + 256, // size of task stack + configMINIMAL_STACK_SIZE + 512, // size of task stack this, // parameters 1, // priority &_isrHandler, @@ -124,7 +128,7 @@ namespace Extenders { uint16_t mask = uint16_t(1) << i; if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { - // log_info("State change pin " << i); + log_info("State change pin " << i); switch (_isrMode[i]) { case RISING: if ((value & mask) == mask) { @@ -168,7 +172,7 @@ namespace Extenders { uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); uint8_t reg = ConfigReg + ((index / 8) & 1); - // log_info("Setup reg " << int(reg) << " with value " << int(value)); + log_info("Setup reg " << int(reg) << " with value " << int(value)); I2CSetValue(_i2cBus, address, reg, value); } @@ -199,11 +203,10 @@ namespace Extenders { _value = ((newValue ^ _invert) & mask) | (_value & ~mask); - // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } else { + log_info("No read, value is " << int(_value)); } - // else { - // log_info("No read, value is " << int(_value)); - // } return (_value & (1ull << index)) != 0; } diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index efc6dded3..7158661de 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -3,6 +3,8 @@ #include "I2CBus.h" +#include + namespace Machine { void I2CBus::validate() const { if (_sda.defined() || _scl.defined()) { @@ -20,8 +22,6 @@ namespace Machine { } void I2CBus::init() { - log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); - auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); @@ -31,9 +31,14 @@ namespace Machine { i2c = &Wire1; } i2c->begin(sdaPin, sclPin /*, _frequency */); + + log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); } int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { + // log_info("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); + i2c->beginTransmission(address); for (size_t i = 0; i < count; ++i) { i2c->write(data[i]); @@ -42,6 +47,9 @@ namespace Machine { } int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { + // log_info("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); + for (size_t i = 0; i < count; ++i) { if (i2c->requestFrom((int)address, 1) != 1) { return i; diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index a55673ae5..febbf51fc 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -5,13 +5,14 @@ #include "../Configuration/Configurable.h" -#include #include +class TwoWire; + namespace Machine { class I2CBus : public Configuration::Configurable { protected: - TwoWire* i2c; + TwoWire* i2c = nullptr; public: I2CBus() = default; @@ -25,6 +26,20 @@ namespace Machine { void validate() const override; void group(Configuration::HandlerBase& handler) override; + /* + typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN + } i2c_err_t; + */ + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 043f0c205..42941e620 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -28,6 +28,51 @@ extern void make_user_commands(); +// FOR TESTING: + +# include +# include + +extern "C" void __pinMode(pinnum_t pin, uint8_t mode); + +uint8_t I2CGetValue(uint8_t address, uint8_t reg) { + Wire.beginTransmission(address); + Wire.write(reg); + auto err = Wire.endTransmission(); // i2c_err_t + + if (Wire.requestFrom((int)address, 1) != 1) { + Uart0.println("Error reading from i2c bus."); + return 0; + } + uint8_t result = Wire.read(); + return result; +} + +void I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + + Wire.beginTransmission(address); + for (size_t i = 0; i < 2; ++i) { + Wire.write(data[i]); + } + auto err = Wire.endTransmission(); // i2c_err_t ?? + + if (err) { + Uart0.println("Error writing to i2c bus; PCA9539 failed. Code: "); + Uart0.println(int(err)); + } +} + +volatile bool fired = false; + +void isrHandler() { + fired = true; +} + +// --- Until here. + void setup() { try { uartInit(); // Setup serial port @@ -39,6 +84,49 @@ void setup() { WebUI::WiFiConfig::reset(); + /* TEST STUFF! */ + + /* + // THIS WORKS: + { + Uart0.println("Basic test of pin extender."); + // Wire.begin(sda , scl, frequency); + Wire.begin(13, 14, 100000); + + Uart0.println("Setup pins:"); + + // 1. Setup pins: + I2CSetValue(0x74, 6, 0xFF); // All input pins + I2CSetValue(0x74, 7, 0xFF); // All input pins + + __pinMode(36, INPUT); + attachInterrupt(36, isrHandler, CHANGE); + + // 2. Read input register: + Uart0.println("Main loop:"); + while (true) { + auto r1 = I2CGetValue(0x74, 0); + auto r2 = I2CGetValue(0x74, 1); + uint16_t v = (uint16_t(r1) << 8) | uint16_t(r2); + Uart0.print("Status: "); + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1 << i); + Uart0.print((v & mask) ? '1' : '0'); + } + + if (fired) { + Uart0.print(" ** ISR"); + fired = false; + } + + Uart0.println(); + + delay(1000); + } + } + */ + /* END TEST STUFF */ + display_init(); // Load settings from non-volatile storage @@ -76,7 +164,10 @@ void setup() { config->_i2c->init(); } - // TODO FIXME: Initialize extenders *here* + // We have to initialize the extenders first, before pins are used + if (config->_extenders) { + config->_extenders->init(); + } config->_stepping->init(); // Configure stepper interrupt timers @@ -125,7 +216,6 @@ void setup() { config->_coolant->init(); config->_probe->init(); } - } catch (const AssertionFailed& ex) { // This means something is terribly broken: log_error("Critical error in main_init: " << ex.what()); diff --git a/FluidNC/src/Pin.cpp b/FluidNC/src/Pin.cpp index 0f825ff76..0cbee91ce 100644 --- a/FluidNC/src/Pin.cpp +++ b/FluidNC/src/Pin.cpp @@ -10,6 +10,7 @@ #include "Pins/VoidPinDetail.h" #include "Pins/I2SOPinDetail.h" #include "Pins/ErrorPinDetail.h" +#include "Pins/ExtPinDetail.h" #include // snprintf() Pins::PinDetail* Pin::undefinedPin = new Pins::VoidPinDetail(); @@ -94,6 +95,16 @@ const char* Pin::parse(StringRange tmp, Pins::PinDetail*& pinImplementation) { pinImplementation = new Pins::VoidPinDetail(); } + if (prefix.startsWith("pinext")) { + if (prefix.length() == 7 && prefix[6] >= '0' && prefix[6] <= '9') { + auto deviceId = prefix[6] - '0'; + pinImplementation = new Pins::ExtPinDetail(deviceId, pinnum_t(pinNumber), parser); + } else { + // For now this should be sufficient, if not we can easily change it to 100 extenders: + return "Incorrect pin extender specification. Expected 'pinext[0-9].[port number]'."; + } + } + if (pinImplementation == nullptr) { log_error("Unknown prefix:" << prefix); return "Unknown pin prefix"; From 774e32792ba30631c1bf2d496c93876f5f9c5006 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Wed, 2 Mar 2022 14:50:21 +0100 Subject: [PATCH 080/100] Rewrite of I2C extender code. --- FluidNC/src/Extenders/I2CExtender.cpp | 408 +++++++++++++++++++++ FluidNC/src/Extenders/I2CExtender.h | 176 +++++++++ FluidNC/src/Extenders/PCA9539.cpp | 274 -------------- FluidNC/src/Extenders/PCA9539.h | 126 ------- FluidNC/src/Machine/I2CBus.cpp | 41 ++- FluidNC/src/Machine/I2CBus.h | 20 +- UnitTests.vcxproj | 5 +- UnitTests.vcxproj.filters | 15 +- X86TestSupport/TestSupport/Wire.h | 1 + X86TestSupport/TestSupport/esp32-hal-i2c.h | 13 + 10 files changed, 648 insertions(+), 431 deletions(-) create mode 100644 FluidNC/src/Extenders/I2CExtender.cpp create mode 100644 FluidNC/src/Extenders/I2CExtender.h delete mode 100644 FluidNC/src/Extenders/PCA9539.cpp delete mode 100644 FluidNC/src/Extenders/PCA9539.h create mode 100644 X86TestSupport/TestSupport/esp32-hal-i2c.h diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp new file mode 100644 index 000000000..8a824a24f --- /dev/null +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -0,0 +1,408 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "I2CExtender.h" + +#include "Extenders.h" +#include "../Logging.h" +#include "../Assert.h" +#include "../Machine/I2CBus.h" + +#include +#include +#include + +namespace Extenders { + I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} + + uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + int err; + if ((err = bus->write(address, ®, 1)) != 0) { + log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + return 0; + } else { + uint8_t result = 0; + if (bus->read(address, &result, 1) != 1) { + log_warn("Cannot read from I2C bus: " + << "no response"); + } + return result; + } + } + void I2CExtender::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + + int err = bus->write(address, data, 2); + + if (err) { + log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + } + } + + void I2CExtender::isrTaskLoopDetail() { + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + int registersPerDevice = _ports / 8; + int claimedValues = 0; + uint8_t commonStatus = _operation; + + // Update everything the first operation + _status = 1; + if (_outputReg != 0xFF) { + _status |= 4; // writes + } + if (_inputReg != 0xFF) { + _status |= 8; // reads + } + + // Main loop for I2C handling: + while (true) { + uint8_t newStatus = 0; + + newStatus = _status.exchange(newStatus); + newStatus |= commonStatus; + + if (newStatus != 0) { + if ((newStatus & 2) != 0) { + break; + } + + // Update config: + if ((newStatus & 1) != 0) { + // First fence! + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + + // Configuration dirty. Update _configuration and _invert. + // + // First check how many u8's are claimed: + claimedValues = 0; + for (int i = 0; i < 8; ++i) { + if (_claimed.bytes[i] != 0) { + claimedValues = i; + } + } + // Configuration: + { + for (int i = 0; i < claimedValues; ++i) { + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + + uint8_t by = _configuration.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + + currentRegister++; + if (currentRegister == registersPerDevice + _operationReg) { + ++address; + } + } + } + // Invert: + if (_invertReg != 0xFF) { + uint8_t currentRegister = _invertReg; + uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { + uint8_t by = _invert.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + + currentRegister++; + if (currentRegister == registersPerDevice + _invertReg) { + ++address; + } + } + } + + // Configuration changed. Writes and reads must be updated. + if (_outputReg != 0xFF) { + newStatus |= 4; // writes + } + if (_inputReg != 0xFF) { + newStatus |= 8; // reads + } + + commonStatus = _operation; + } + + // Handle writes: + if ((newStatus & 4) != 0) { + uint8_t currentRegister = _outputReg; + uint8_t address = _address; + + bool handleInvertSoftware = (_invertReg == 0xFF); + + auto toWrite = _dirtyWrite.exchange(0); + for (int i = 0; i < claimedValues; ++i) { + if ((toWrite & (1 << i)) != 0) { + uint8_t by = handleInvertSoftware ? (_output.bytes[i] ^ _invert.bytes[i]) : _output.bytes[i]; + I2CSetValue(this->_i2cBus, address, currentRegister, by); + } + + currentRegister++; + if (currentRegister == registersPerDevice + _outputReg) { + ++address; + } + } + } + + // Handle reads: + if ((newStatus & 8) != 0) { + uint8_t currentRegister = _inputReg; + uint8_t address = _address; + + // If we don't have an ISR, we must update everything. Otherwise, we can cherry pick: + bool handleInvertSoftware = (_invertReg == 0xFF); + + for (int i = 0; i < claimedValues; ++i) { + auto oldByte = _input.bytes[i]; + auto newByte = I2CGetValue(this->_i2cBus, address, currentRegister); + if (handleInvertSoftware) { + newByte ^= _invert.bytes[i]; + } + + if (oldByte != newByte) { + // Handle ISR's: + _input.bytes[i] = newByte; + int offset = claimedValues * 8; + for (int j = 0; j < 8; ++j) { + auto isr = _isrData[offset + j]; + if (isr.defined()) { + auto mask = uint8_t(1 << j); + auto o = (oldByte & mask); + auto n = (newByte & mask); + if (o != n) { + isr.callback(isr.data); + } + } + } + } + + currentRegister++; + if (currentRegister == registersPerDevice + _invertReg) { + ++address; + } + } + } + } + + vTaskDelay(TaskDelayBetweenIterations); + } + } + + void I2CExtender::isrTaskLoop(void* arg) { static_cast(arg)->isrTaskLoopDetail(); } + + void I2CExtender::claim(pinnum_t index) { + Assert(index >= 0 && index < 64, "I2CExtender IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed.value & mask) == 0, "I2CExtender IO port %d is already used.", index); + + _claimed.value |= mask; + } + + void I2CExtender::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed.value &= ~mask; + } + + void I2CExtender::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "I2CExtender works through I2C, but I2C is not configured."); + + // We cannot validate _i2cBus, because that's initialized during `init`. + Assert(_device != int(I2CExtenderDevice::Unknown), "I2C device type is unknown. Cannot continue initializing extender."); + } + + void I2CExtender::group(Configuration::HandlerBase& handler) { + // device: pca9539 + // device_id: 0 + // interrupt: gpio.36 + handler.item("device", _device); + handler.item("device_id", _deviceId); + handler.item("interrupt", _interruptPin); + } + + void I2CExtender::interruptHandler(void* arg) { + auto ext = static_cast(arg); + ext->_status |= 8; + } + + void I2CExtender::init() { + Assert(_isrHandler == nullptr, "Init has already been called on I2C extender."); + + switch (I2CExtenderDevice(_device)) { + case I2CExtenderDevice::PCA9539: + // See data sheet page 7+: + _address = 0x74 + _deviceId; + _ports = 16; + _inputReg = 0; + _outputReg = 2; + _invertReg = 4; + _operationReg = 6; + break; + + default: + Assert(false, "Pin extender device is not supported!"); + break; + } + + xTaskCreatePinnedToCore(isrTaskLoop, // task + "i2cHandler", // name for task + configMINIMAL_STACK_SIZE + 512, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + if (_interruptPin.defined()) { + _interruptPin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + _interruptPin.attachInterrupt(interruptHandler, FALLING, this); + } + } + + void IRAM_ATTR I2CExtender::setupPin(pinnum_t index, Pins::PinAttributes attr) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + _usedIORegisters |= uint8_t(1 << (index / 8)); + + uint64_t mask = 1ull << index; + + if (attr.has(Pins::PinAttributes::Input)) { + _configuration.value |= mask; + + if (attr.has(Pins::PinAttributes::PullUp)) { + _output.value |= mask; + } else if (attr.has(Pins::PinAttributes::PullDown)) { + _output.value &= ~mask; + } + } else if (attr.has(Pins::PinAttributes::Output)) { + _configuration.value &= ~mask; + + if (attr.has(Pins::PinAttributes::InitialOn)) { + _output.value |= mask; + } + } + + if (attr.has(Pins::PinAttributes::ActiveLow)) { + _invert.value |= mask; + } + + // Ignore the ISR flag. ISR is fine. + + // Trigger an update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + _status |= 1; + } + + void IRAM_ATTR I2CExtender::writePin(pinnum_t index, bool high) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + uint64_t mask = 1ull << index; + if (high) { + _output.value |= mask; + } else { + _output.value &= ~mask; + } + + uint8_t dirtyMask = uint8_t(1 << (index / 8)); + _dirtyWriteBuffer |= dirtyMask; + + // Note that _status is *not* updated! flushWrites takes care of this! + } + + bool IRAM_ATTR I2CExtender::readPin(pinnum_t index) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + // There are two possibilities here: + // 1. We use an ISR, and that we can just use the information as-is as long as it's in sync. + // The ISR itself triggers the update. + // 2. We don't use an ISR and need to update from I2C before we can reliably use the value. + + if (!_interruptPin.defined()) { + _status |= 8; + } + while (_status != 0) { + vTaskDelay(1); + } + + // Use the value: + return ((_input.value >> index) & 1) == 1; + } + + void IRAM_ATTR I2CExtender::flushWrites() { + auto writeMask = _dirtyWriteBuffer.exchange(0); + + _dirtyWrite |= writeMask; + _status |= 4; + + while (_status != 0) { + vTaskDelay(1); + } + } + + void I2CExtender::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + Assert(mode == CHANGE, "Only mode CHANGE is allowed for pin extender ISR's."); + Assert(index < 64 && index >= 0, "Pin index out of range"); + + ISRData& data = _isrData[index]; + data.callback = callback; + data.data = arg; + + // Update continuous operation. + _operation &= ~8; + if (!_interruptPin.defined()) { + _operation |= 8 | 16; + } + + // Trigger task configuration update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // write fence first! + _status |= 1; + } + + void I2CExtender::detachInterrupt(pinnum_t index) { + Assert(index < 64 && index >= 0, "Pin index out of range"); + + ISRData& data = _isrData[index]; + data.callback = nullptr; + data.data = nullptr; + + // Check if we still need to ISR everything. Use a temporary to ensure thread safety: + auto newop = _operation; + newop &= ~8; + if (!_interruptPin.defined()) { + for (int i = 0; i < 64; ++i) { + if (_isrData[i].defined()) { + newop |= 8 | 16; + } + } + } + _operation = newop; + + // Trigger task configuration update: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // write fence first! + _status |= 1; + } + + const char* I2CExtender::name() const { return "i2c_extender"; } + + I2CExtender::~I2CExtender() { + // The task might have allocated temporary data, so we cannot just destroy it: + _status |= 2; + + // Detach the interrupt pin: + if (_interruptPin.defined()) { + _interruptPin.detachInterrupt(); + } + + // Give enough time for the task to stop: + vTaskDelay(TaskDelayBetweenIterations * 10); + + // Should be safe now to stop. + _isrHandler = nullptr; + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("i2c_extender"); + } +} diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h new file mode 100644 index 000000000..b98ff6037 --- /dev/null +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -0,0 +1,176 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + enum class I2CExtenderDevice { + Unknown, + PCA9539, + }; + + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + // + // How this class works... + // + // A single device has a bunch of pins, normally 8 or 16. These devices also have an address. + // The maximum number of extender I/O pins that we currently support is currently 64. Note that + // the I2C frequency doesn't really matter if you have the ISR handled. We limit the maximum number + // of ports to 64 here, which basically means you *can* wire multiple devices on a single ISR line + // by making good use of the addresses. + // + // Keep in mind that low latency is only possible if you do things correctly with placement on the + // PCB, and with designating I/O ports for very specific purposes. If not, the firmware won't know + // what an interrupt means, and has to figure it out before it can take action. + // + // A typical configuration looks like: + // + // device: pca9539 + // device_id: 0 + // interrupt: gpio.36 + // + class I2CExtender : public PinExtenderDriver { + struct RegisterSet { + union { + uint64_t value = 0; + uint8_t bytes[8]; + }; + }; + + struct ISRData { + using ISRCallback = void (*)(void*); + ISRData() : callback(nullptr), data(nullptr) {} + + ISRCallback callback; + void* data; + + inline bool defined() const { return callback != nullptr; } + }; + + // Device info: + int _device = int(I2CExtenderDevice::Unknown); + int _deviceId = 0; + + static const int TaskDelayBetweenIterations = 10; + + // Operation and status work together and form a common bitmask. Operation is just not reset, + // while status is. + uint8_t _operation = 0; + + // This information is filled based on the "device" and "device_id" during initialization: + uint8_t _bus = 0; + uint8_t _address = 0x74; + uint8_t _ports = 16; + uint8_t _invertReg = 0xFF; + uint8_t _operationReg = 0xFF; + uint8_t _inputReg = 0xFF; + uint8_t _outputReg = 0xFF; + Pin _interruptPin; + + RegisterSet _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Current register values: + RegisterSet _configuration; + RegisterSet _invert; + volatile RegisterSet _input; + volatile RegisterSet _output; + + // I2C communications within an ISR is not a good idea, it will crash everything. We offload + // the communications using a task queue. Dirty tells which devices and registers to poll. + // Every I2C roundtrip is always responsible for 8 bytes. + TaskHandle_t _isrHandler = nullptr; + + uint8_t _usedIORegisters; + std::atomic _dirtyWriteBuffer; + std::atomic _dirtyWrite; + + // Status is a bitmask that tells the task handle what needs to happen during the next roundtrip. + // This works together with 'operation'. + std::atomic _status; + ISRData _isrData[64]; + + static void isrTaskLoop(void* arg); + void isrTaskLoopDetail(); + + public: + I2CExtender(); + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + + void group(Configuration::HandlerBase& handler) override; + + static void interruptHandler(void* arg); + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~I2CExtender(); + }; +} diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp deleted file mode 100644 index eea3cb056..000000000 --- a/FluidNC/src/Extenders/PCA9539.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) 2021 - Stefan de Bruijn -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. - -#include "Extenders.h" -#include "PCA9539.h" -#include "../Logging.h" - -#include -#include - -namespace Extenders { - void PCA9539::claim(pinnum_t index) { - Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); - - uint64_t mask = uint64_t(1) << index; - Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); - - _claimed |= mask; - } - - void PCA9539::free(pinnum_t index) { - uint64_t mask = uint64_t(1) << index; - _claimed &= ~mask; - } - - uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { - Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); - - auto err = bus->write(address, ®, 1); - - if (err) { - log_info("Error writing to i2c bus. Code: " << err); - return 0; - } - - uint8_t inputData; - if (bus->read(address, &inputData, 1) != 1) { - log_info("Error reading from i2c bus."); - } - - return inputData; - } - - void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { - Assert(bus != nullptr, "I2C bus is not initialized; cannot continue."); - - uint8_t data[2]; - data[0] = reg; - data[1] = uint8_t(value); - auto err = bus->write(address, data, 2); - - if (err) { - log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); - } - } - - void PCA9539::validate() const { - auto i2c = config->_i2c; - Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); - } - - void PCA9539::group(Configuration::HandlerBase& handler) { - handler.item("interrupt0", _isrData[0]._pin); - handler.item("interrupt1", _isrData[1]._pin); - handler.item("interrupt2", _isrData[2]._pin); - handler.item("interrupt3", _isrData[3]._pin); - } - - void PCA9539::isrTaskLoop(void* arg) { - auto inst = static_cast(arg); - while (true) { - void* ptr; - if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { - ISRData* valuePtr = static_cast(ptr); - log_info("PCA state change ISR"); - valuePtr->updateValueFromDevice(); - } - } - } - - void PCA9539::init() { - this->_i2cBus = config->_i2c; - - _isrQueue = xQueueCreate(16, sizeof(void*)); - xTaskCreatePinnedToCore(isrTaskLoop, // task - "isr_handler", // name for task - configMINIMAL_STACK_SIZE + 512, // size of task stack - this, // parameters - 1, // priority - &_isrHandler, - SUPPORT_TASK_CORE // core - ); - - for (int i = 0; i < 4; ++i) { - auto& data = _isrData[i]; - - data._address = uint8_t(0x74 + i); - data._container = this; - data._valueBase = reinterpret_cast(&_value) + i; - - // Update the value first by reading it: - data.updateValueFromDevice(); - - if (!data._pin.undefined()) { - data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); - - // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. - data._pin.attachInterrupt(updatePCAState, FALLING, &data); - } else { - // Reset valueBase so we know it's not bound to an ISR: - data._valueBase = nullptr; - } - } - } - - void PCA9539::ISRData::updateValueFromDevice() { - const uint8_t InputReg = 0; - auto i2cBus = _container->_i2cBus; - - auto r1 = I2CGetValue(i2cBus, _address, InputReg); - auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); - uint16_t oldValue = *_valueBase; - uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); - *_valueBase = value; - - if (_hasISR) { - for (int i = 0; i < 16; ++i) { - uint16_t mask = uint16_t(1) << i; - - if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { - log_info("State change pin " << i); - switch (_isrMode[i]) { - case RISING: - if ((value & mask) == mask) { - _isrCallback[i](_isrArgument); - } - break; - case FALLING: - if ((value & mask) == 0) { - _isrCallback[i](_isrArgument); - } - break; - case CHANGE: - _isrCallback[i](_isrArgument); - break; - } - } - } - } - } - - void PCA9539::updatePCAState(void* ptr) { - ISRData* valuePtr = static_cast(ptr); - - BaseType_t xHigherPriorityTaskWoken = false; - xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); - } - - void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { - bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); - bool output = attr.has(Pins::PinAttributes::Output); - - uint64_t mask = uint64_t(1) << index; - _invert = (_invert & ~mask) | (activeLow ? mask : 0); - _configuration = (_configuration & ~mask) | (output ? 0 : mask); - - const uint8_t deviceId = index / 16; - - const uint8_t ConfigReg = 6; - uint8_t address = 0x74 + deviceId; - - uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); - uint8_t reg = ConfigReg + ((index / 8) & 1); - - log_info("Setup reg " << int(reg) << " with value " << int(value)); - - I2CSetValue(_i2cBus, address, reg, value); - } - - void PCA9539::writePin(pinnum_t index, bool high) { - uint64_t mask = uint64_t(1) << index; - uint64_t oldVal = _value; - uint64_t newVal = high ? mask : uint64_t(0); - _value = (_value & ~mask) | newVal; - - _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); - } - - bool PCA9539::readPin(pinnum_t index) { - uint8_t reg = uint8_t(index / 8); - uint8_t deviceId = reg / 2; - - // If it's handled by the ISR, we don't need to read anything from the device. - // Otherwise, we do. Check: - if (_isrData[deviceId]._valueBase == nullptr) { - const uint8_t InputReg = 0; - uint8_t address = 0x74 + deviceId; - - auto readReg = InputReg + (reg & 1); - auto value = I2CGetValue(_i2cBus, address, readReg); - uint64_t newValue = uint64_t(value) << (int(reg) * 8); - uint64_t mask = uint64_t(0xff) << (int(reg) * 8); - - _value = ((newValue ^ _invert) & mask) | (_value & ~mask); - - log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); - } else { - log_info("No read, value is " << int(_value)); - } - - return (_value & (1ull << index)) != 0; - } - - void PCA9539::flushWrites() { - uint64_t write = _value ^ _invert; - for (int i = 0; i < 8; ++i) { - if ((_dirtyRegisters & (1 << i)) != 0) { - const uint8_t OutputReg = 2; - uint8_t address = 0x74 + (i / 2); - - uint8_t val = uint8_t(write >> (8 * i)); - uint8_t reg = OutputReg + (i & 1); - I2CSetValue(_i2cBus, address, reg, val); - } - } - - _dirtyRegisters = 0; - } - - // ISR's: - void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { - int device = index / 16; - int pinNumber = index % 16; - - Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); - - _isrData[device]._isrCallback[pinNumber] = callback; - _isrData[device]._isrArgument[pinNumber] = arg; - _isrData[device]._isrMode[pinNumber] = mode; - _isrData[device]._hasISR = true; - } - - void PCA9539::detachInterrupt(pinnum_t index) { - int device = index / 16; - int pinNumber = index % 16; - - _isrData[device]._isrCallback[pinNumber] = nullptr; - _isrData[device]._isrArgument[pinNumber] = nullptr; - _isrData[device]._isrMode[pinNumber] = 0; - - bool hasISR = false; - for (int i = 0; i < 16; ++i) { - hasISR |= (_isrData[device]._isrArgument[i] != nullptr); - } - _isrData[device]._hasISR = hasISR; - } - - const char* PCA9539::name() const { return "pca9539"; } - - PCA9539 ::~PCA9539() { - for (int i = 0; i < 4; ++i) { - auto& data = _isrData[i]; - - if (!data._pin.undefined()) { - data._pin.detachInterrupt(); - } - } - } - - // Register extender: - namespace { - PinExtenderFactory::InstanceBuilder registration("pca9539"); - } -} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h deleted file mode 100644 index d90068370..000000000 --- a/FluidNC/src/Extenders/PCA9539.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2021 - Stefan de Bruijn -// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. - -#pragma once - -#include "PinExtenderDriver.h" -#include "../Configuration/Configurable.h" -#include "../Machine/MachineConfig.h" -#include "../Machine/I2CBus.h" -#include "../Platform.h" - -#include - -namespace Pins { - class PCA9539PinDetail; -} - -namespace Extenders { - // Pin extenders... - // - // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address - // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 - // separate registers, so that's a total of 16*4 = 64 values. - // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf - // Speed: 400 kHz - // - // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single - // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. - // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf - // Speed: 400 kHz - // - // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means - // that *some* pin has changed state. We don't know which one that was obviously. - // However, we can then query the individual pins (thereby resetting them) and throwing - // the results as individual ISR's. - // - // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the - // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless - // to say, this is usually a bad idea, because things like endstops become much slower - // as a result. For now, I just felt like not supporting it. - // - // The MCP23017 has two interrupt lines, one for register A and register B. Apart from - // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, - // so that's a total of 8 * 16 = 128 I/O ports. - // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf - // Speed: 100 kHz, 400 kHz, 1.7 MHz. - // - // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 - // seems to be the same, but 8-bit. - // - // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state - // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes - // pullups and schmitt triggers. - // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf - class PCA9539 : public PinExtenderDriver { - friend class Pins::PCA9539PinDetail; - - // Address can be set for up to 4 devices. Each device supports 16 pins. - - static const int numberPins = 16 * 4; - uint64_t _claimed; - - Machine::I2CBus* _i2cBus; - - static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); - static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); - - // Registers: - // 4x16 = 64 bits. Fits perfectly into an uint64. - uint64_t _configuration = 0; - uint64_t _invert = 0; - volatile uint64_t _value = 0; - - // 4 devices, 2 registers per device. 8 bits is enough: - uint8_t _dirtyRegisters = 0; - - QueueHandle_t _isrQueue = nullptr; - TaskHandle_t _isrHandler = nullptr; - - static void isrTaskLoop(void* arg); - - struct ISRData { - ISRData() = default; - - Pin _pin; - PCA9539* _container = nullptr; - volatile uint16_t* _valueBase = nullptr; - uint8_t _address = 0; - - typedef void (*ISRCallback)(void*); - - bool _hasISR = false; - ISRCallback _isrCallback[16] = { 0 }; - void* _isrArgument[16] = { 0 }; - int _isrMode[16] = { 0 }; - - void IRAM_ATTR updateValueFromDevice(); - }; - - ISRData _isrData[4]; - static void IRAM_ATTR updatePCAState(void* ptr); - - public: - PCA9539() = default; - - void claim(pinnum_t index) override; - void free(pinnum_t index) override; - - void validate() const override; - void group(Configuration::HandlerBase& handler) override; - - void init(); - - void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; - void IRAM_ATTR writePin(pinnum_t index, bool high) override; - bool IRAM_ATTR readPin(pinnum_t index) override; - void IRAM_ATTR flushWrites() override; - - void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; - void detachInterrupt(pinnum_t index) override; - - const char* name() const override; - - ~PCA9539(); - }; -} diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index 7158661de..dcae580d3 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -4,6 +4,7 @@ #include "I2CBus.h" #include +#include namespace Machine { void I2CBus::validate() const { @@ -25,30 +26,56 @@ namespace Machine { auto sdaPin = _sda.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); auto sclPin = _scl.getNative(Pin::Capabilities::Native | Pin::Capabilities::Input | Pin::Capabilities::Output); + Assert(_busNumber == 0 || _busNumber == 1, "Bus # has to be 0 or 1; the ESP32 does not have more i2c peripherals."); + if (_busNumber == 0) { i2c = &Wire; } else { i2c = &Wire1; } - i2c->begin(sdaPin, sclPin /*, _frequency */); + i2c->begin(int(sdaPin), int(sclPin), _frequency); - log_info("I2C SDA:" << _sda.name() << ", SCL:" << _scl.name() << ", Bus:" << _busNumber); + log_info("I2C SDA: " << _sda.name() << ", SCL: " << _scl.name() << ", Freq: " << _frequency << ", Bus #: " << _busNumber); } + const char* I2CBus::ErrorDescription(int code) { + switch (code) { + case I2C_ERROR_OK: + return "ok"; + case I2C_ERROR_DEV: + return "general device error"; + case I2C_ERROR_ACK: + return "no ack returned by device"; + case I2C_ERROR_TIMEOUT: + return "timeout"; + case I2C_ERROR_BUS: + return "bus error"; + case I2C_ERROR_BUSY: + return "device busy"; + case I2C_ERROR_MEMORY: + return "insufficient memory"; + case I2C_ERROR_CONTINUE: + return "continue"; + case I2C_ERROR_NO_BEGIN: + return "begin transmission missing"; + default: + return "unknown"; + } + } int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { - // log_info("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " - // << (i2c ? "non null" : "null")); + // log_debug("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); i2c->beginTransmission(address); for (size_t i = 0; i < count; ++i) { i2c->write(data[i]); } - return i2c->endTransmission(); // i2c_err_t ?? + return i2c->endTransmission(); // i2c_err_t, see header file } int I2CBus::read(uint8_t address, uint8_t* data, size_t count) { - // log_info("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " - // << (i2c ? "non null" : "null")); + // log_debug("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " + // << (i2c ? "non null" : "null")); for (size_t i = 0; i < count; ++i) { if (i2c->requestFrom((int)address, 1) != 1) { diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index febbf51fc..f7ddab35e 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -20,27 +20,15 @@ namespace Machine { int _busNumber = 0; Pin _sda; Pin _scl; - uint32_t _frequency = 0; + uint32_t _frequency = 100000; void init(); void validate() const override; void group(Configuration::HandlerBase& handler) override; - /* - typedef enum { - I2C_ERROR_OK=0, - I2C_ERROR_DEV, - I2C_ERROR_ACK, - I2C_ERROR_TIMEOUT, - I2C_ERROR_BUS, - I2C_ERROR_BUSY, - I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE, - I2C_ERROR_NO_BEGIN - } i2c_err_t; - */ - - int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); // return i2c_err_t ?? Or is it mapped? TODO FIXME! + static const char* ErrorDescription(int code); + + int IRAM_ATTR write(uint8_t address, const uint8_t* data, size_t count); int IRAM_ATTR read(uint8_t address, uint8_t* data, size_t count); ~I2CBus() = default; diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 93d739c8d..5f2cffa0b 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -34,7 +34,7 @@ - + @@ -182,6 +182,7 @@ + @@ -218,7 +219,7 @@ - + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index d8f5ac631..72c058c0e 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -582,9 +582,6 @@ src\Machine - - src\Extenders - src\Extenders @@ -597,6 +594,12 @@ src\Pins + + X86TestSupport + + + src\Extenders + @@ -1016,9 +1019,6 @@ src\Machine - - src\Extenders - src\Extenders @@ -1031,6 +1031,9 @@ src\Pins + + src\Extenders + diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index aa9f01dcf..98cddae8a 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -1,6 +1,7 @@ #pragma once #include +#include "esp32-hal-i2c.h" #include "Stream.h" class TwoWire : public Stream { diff --git a/X86TestSupport/TestSupport/esp32-hal-i2c.h b/X86TestSupport/TestSupport/esp32-hal-i2c.h new file mode 100644 index 000000000..066108aad --- /dev/null +++ b/X86TestSupport/TestSupport/esp32-hal-i2c.h @@ -0,0 +1,13 @@ +#pragma once + +typedef enum { + I2C_ERROR_OK = 0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; From 13bee53f545932eb8e5cc9d7e18dae7dad7dd79b Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Wed, 2 Mar 2022 15:07:40 +0100 Subject: [PATCH 081/100] Fixed a few small bugs. It's not working yet, but getting there... --- FluidNC/src/Extenders/I2CExtender.cpp | 5 ++++- FluidNC/src/Extenders/I2CExtender.h | 2 -- FluidNC/src/Machine/I2CBus.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 8a824a24f..4c5b32e69 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -13,6 +13,8 @@ #include namespace Extenders { + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { @@ -216,7 +218,7 @@ namespace Extenders { // device: pca9539 // device_id: 0 // interrupt: gpio.36 - handler.item("device", _device); + handler.item("device", _device, i2cDevice); handler.item("device_id", _deviceId); handler.item("interrupt", _interruptPin); } @@ -228,6 +230,7 @@ namespace Extenders { void I2CExtender::init() { Assert(_isrHandler == nullptr, "Init has already been called on I2C extender."); + this->_i2cBus = config->_i2c; switch (I2CExtenderDevice(_device)) { case I2CExtenderDevice::PCA9539: diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index b98ff6037..c94e25f99 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -21,8 +21,6 @@ namespace Extenders { PCA9539, }; - EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; - // Pin extenders... // // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index dcae580d3..daf5120a2 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -18,7 +18,7 @@ namespace Machine { void I2CBus::group(Configuration::HandlerBase& handler) { handler.item("sda", _sda); handler.item("scl", _scl); - handler.item("busNumber", _busNumber); + handler.item("bus", _busNumber); handler.item("frequency", _frequency); } From d7487f7618b1b5688bb2201c926633f58c9abb28 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 12:23:26 +0100 Subject: [PATCH 082/100] Added first I2C extender unit tests --- FluidNC/src/Extenders/I2CExtender.cpp | 51 +-- FluidNC/src/Extenders/I2CExtender.h | 9 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 340 +++++++++++++++++++ UnitTests.vcxproj | 1 + UnitTests.vcxproj.filters | 6 + X86TestSupport/TestSupport/Capture.h | 2 +- X86TestSupport/TestSupport/Wire.cpp | 146 ++++++++ X86TestSupport/TestSupport/Wire.h | 92 ++--- X86TestSupport/TestSupport/freertos/Task.cpp | 1 + 9 files changed, 582 insertions(+), 66 deletions(-) create mode 100644 FluidNC/test/Extender/I2CExtenderTests.cpp diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 4c5b32e69..b7dea07c7 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -15,7 +15,7 @@ namespace Extenders { EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; - I2CExtender::I2CExtender() : _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} + I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { int err; @@ -67,6 +67,7 @@ namespace Extenders { if (newStatus != 0) { if ((newStatus & 2) != 0) { + _status = 0; break; } @@ -81,34 +82,34 @@ namespace Extenders { claimedValues = 0; for (int i = 0; i < 8; ++i) { if (_claimed.bytes[i] != 0) { - claimedValues = i; + claimedValues = i + 1; } } - // Configuration: - { + // Invert: + if (_invertReg != 0xFF) { + uint8_t currentRegister = _invertReg; + uint8_t address = _address; for (int i = 0; i < claimedValues; ++i) { - uint8_t currentRegister = _operationReg; - uint8_t address = _address; - - uint8_t by = _configuration.bytes[i]; + uint8_t by = _invert.bytes[i]; I2CSetValue(this->_i2cBus, address, currentRegister, by); currentRegister++; - if (currentRegister == registersPerDevice + _operationReg) { + if (currentRegister == registersPerDevice + _invertReg) { ++address; } } } - // Invert: - if (_invertReg != 0xFF) { - uint8_t currentRegister = _invertReg; - uint8_t address = _address; + // Configuration: + { for (int i = 0; i < claimedValues; ++i) { - uint8_t by = _invert.bytes[i]; + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + + uint8_t by = _configuration.bytes[i]; I2CSetValue(this->_i2cBus, address, currentRegister, by); currentRegister++; - if (currentRegister == registersPerDevice + _invertReg) { + if (currentRegister == registersPerDevice + _operationReg) { ++address; } } @@ -116,7 +117,8 @@ namespace Extenders { // Configuration changed. Writes and reads must be updated. if (_outputReg != 0xFF) { - newStatus |= 4; // writes + newStatus |= 4; // writes + _dirtyWrite = 0xFF; // everything is dirty. } if (_inputReg != 0xFF) { newStatus |= 8; // reads @@ -248,6 +250,9 @@ namespace Extenders { break; } + // Ensure data is available: + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + xTaskCreatePinnedToCore(isrTaskLoop, // task "i2cHandler", // name for task configMINIMAL_STACK_SIZE + 512, // size of task stack @@ -300,15 +305,19 @@ namespace Extenders { void IRAM_ATTR I2CExtender::writePin(pinnum_t index, bool high) { Assert(index < 64 && index >= 0, "Pin index out of range"); - uint64_t mask = 1ull << index; + uint64_t mask = 1ull << index; + auto oldValue = _output.value; if (high) { _output.value |= mask; } else { _output.value &= ~mask; } - uint8_t dirtyMask = uint8_t(1 << (index / 8)); - _dirtyWriteBuffer |= dirtyMask; + // Did something change? + if (oldValue != _output.value) { + uint8_t dirtyMask = uint8_t(1 << (index / 8)); + _dirtyWriteBuffer |= dirtyMask; + } // Note that _status is *not* updated! flushWrites takes care of this! } @@ -398,7 +407,9 @@ namespace Extenders { } // Give enough time for the task to stop: - vTaskDelay(TaskDelayBetweenIterations * 10); + for (int i = 0; i < 10 && _status != 0; ++i) { + vTaskDelay(TaskDelayBetweenIterations); + } // Should be safe now to stop. _isrHandler = nullptr; diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index c94e25f99..222226d53 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -144,18 +144,16 @@ namespace Extenders { static void isrTaskLoop(void* arg); void isrTaskLoopDetail(); + static void interruptHandler(void* arg); + public: I2CExtender(); void claim(pinnum_t index) override; void free(pinnum_t index) override; - void validate() const override; - void group(Configuration::HandlerBase& handler) override; - - static void interruptHandler(void* arg); - + void validate() const override; void init(); void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; @@ -164,7 +162,6 @@ namespace Extenders { void IRAM_ATTR flushWrites() override; void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; - void detachInterrupt(pinnum_t index) override; const char* name() const override; diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp new file mode 100644 index 000000000..2db277858 --- /dev/null +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -0,0 +1,340 @@ +#include "../TestFramework.h" + +#include +#include +#include +#include +#include + +#include "Capture.h" + +namespace Configuration { + Test(I2CExtender, I2CBasics) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + + bus.validate(); + bus.init(); + + Wire.Clear(); + + Assert(0 == bus.write(1, reinterpret_cast("aap"), 3), "Bad write"); + auto data = Wire.Receive(); + + Assert(data.size() == 3, "Expected 3 bytes"); + data.push_back(0); + Assert(!strcmp(reinterpret_cast(data.data()), "aap"), "Incorrect data read"); + + uint8_t tmp[4]; + tmp[3] = 0; + Assert(bus.read(1, tmp, 3) == 0, "Expected no data available for read"); + + std::vector tmp2; + tmp2.push_back(uint8_t('p')); + tmp2.push_back(uint8_t('i')); + tmp2.push_back(uint8_t('m')); + + Wire.Send(tmp2); + Assert(bus.read(1, tmp, 3) == 3, "Expected 3 bytes data available for read"); + Assert(bus.read(1, tmp, 3) == 0, "Expected no data available for read"); + Assert(!strcmp(reinterpret_cast(tmp), "pim"), "Incorrect data read"); + } + + // Helper class for initialization of I2C extender + class FakeInitHandler : public Configuration::HandlerBase { + bool hasISR_; + + protected: + void enterSection(const char* name, Configurable* value) override {} + bool matchesUninitialized(const char* name) override { return true; } + + public: + FakeInitHandler(bool hasISR) : hasISR_(hasISR) {} + + void item(const char* name, float& value, float minValue = -3e38, float maxValue = 3e38) override {} + void item(const char* name, std::vector& value) override {} + void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override {} + void item(const char* name, Pin& value) override { + if (!strcmp(name, "interrupt") && hasISR_) { + value = Pin::create("gpio.15"); + } + } + void item(const char* name, IPAddress& value) override {} + void item(const char* name, int& value, EnumItem* e) override { + if (!strcmp(name, "device")) { + value = int(Extenders::I2CExtenderDevice::PCA9539); + } + } + + void item(const char* name, String& value, int minLength = 0, int maxLength = 255) override {} + + HandlerType handlerType() override { return HandlerType::Parser; } + + void item(const char* name, bool& value) override {} + void item(const char* name, int32_t& value, int32_t minValue = 0, int32_t maxValue = INT32_MAX) override { + if (!strcmp(name, "device_id")) { + value = 0; + } + } + }; + + Test(I2CExtender, InitDeinit) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + } + + Test(I2CExtender, ClaimRelease) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + i2c.claim(1); + i2c.claim(0); + AssertThrow(i2c.claim(1)); + i2c.claim(2); + AssertThrow(i2c.claim(64)); + AssertThrow(i2c.claim(-1)); + i2c.free(1); + i2c.free(1); + i2c.claim(1); + AssertThrow(i2c.claim(1)); + i2c.free(0); + i2c.free(1); + i2c.free(2); + } + + class Roundtrip { + uint32_t before; + + public: + Roundtrip() { before = Capture::instance().current(); } + + ~Roundtrip() { + while (Capture::instance().current() < before + 1) { + delay(10); + } + } + }; + + Test(I2CExtender, SetupPin) { + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + // Expected register values (see datasheet): + // + // 4 invert + // 1 = invert, 0 = normal + // + // 6 config + // 1 = input, 0 = output + // + // 2 write + // 1 = high, 0 = low + // + // 0 read + // high = 1, low = 0 + + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x01); + + i2c.claim(0); + i2c.setupPin(0, Pin::Attr::Output); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), + uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Read will trigger an update, because we don't have an ISR + { + Wire.Send(0x01); + bool readPin = i2c.readPin(0); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test write pin: + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + } + { + // Write to set it 'low'. + i2c.writePin(0, false); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 0, "Expected write reg 0 = 0"); + } + { + // Write to set it 'low'. It's already low, no-op. + i2c.writePin(0, false); + i2c.flushWrites(); + // no-op. + } + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + } + + // NOTE: We ended with setting pin #0 to 'high' = 0x01 + + // Setup pin for reading: + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + + i2c.claim(1); + i2c.setupPin(1, Pin::Attr::Input); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), + uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Setup another pin for reading with an invert mask and a PU: + { + Roundtrip rt; + + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x04); + + i2c.claim(2); + i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + } + { + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), + uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Test read pin: + { + Wire.Send(0x02); + bool readPin = i2c.readPin(1); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test read pin: + { + Wire.Send(0x02); + bool readPin = i2c.readPin(2); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv[0] == 0, "Expected read"); + Assert(readPin == false, "Expected 'true' on pin"); + } + } +} diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index 5f2cffa0b..f74585854 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -279,6 +279,7 @@ + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 72c058c0e..716078b48 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -49,6 +49,9 @@ {2b9b9202-4bb3-450e-a90b-99f042de0af2} + + {4f2af482-2e54-41bf-b698-1ab77a1a5ea3} + @@ -1034,6 +1037,9 @@ src\Extenders + + test\Extender + diff --git a/X86TestSupport/TestSupport/Capture.h b/X86TestSupport/TestSupport/Capture.h index f8d3be1df..e9ce30a7c 100644 --- a/X86TestSupport/TestSupport/Capture.h +++ b/X86TestSupport/TestSupport/Capture.h @@ -21,7 +21,7 @@ class Capture { Capture() = default; std::vector events; - uint32_t currentTime = 0; + volatile uint32_t currentTime = 0; public: static Capture& instance() { diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 046c93eda..69373b035 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -2,3 +2,149 @@ TwoWire Wire(0); TwoWire Wire1(1); + +TwoWire::TwoWire(uint8_t bus_num) {} +TwoWire::~TwoWire() {} + +// For unit tests: +void TwoWire::Send(std::vector data) { + for (auto it : data) { + Send(it); + } +} +void TwoWire::Send(uint8_t value) { + receivedData.push_back(value); +} +std::vector TwoWire::Receive() { + std::vector data; + std::swap(sentData, data); + return data; +} + +void TwoWire::Clear() { + sentData.clear(); + receivedData.clear(); +} + +// TwoWire interface: + +// call setPins() first, so that begin() can be called without arguments from libraries +bool TwoWire::setPins(int sda, int scl) { + return true; +} + +bool TwoWire::begin(int sda, int scl, uint32_t frequency) { + return true; +} // returns true, if successful init of i2c bus + +bool TwoWire::begin(uint8_t slaveAddr, int sda, int scl, uint32_t frequency) { + return true; +} +bool TwoWire::end() { + return true; +} + +void TwoWire::setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms +uint16_t TwoWire::getTimeOut() { + return 0; +} + +bool TwoWire::setClock(uint32_t) { + return true; +} +uint32_t TwoWire::getClock() { + return 0; +} + +void TwoWire::beginTransmission(uint16_t address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} +void TwoWire::beginTransmission(uint8_t address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} +void TwoWire::beginTransmission(int address) { + Assert(!inTransmission, "Already in a transmission"); + inTransmission = true; +} + +uint8_t TwoWire::endTransmission(bool sendStop) { + Assert(inTransmission, "Should be in a transmission"); + inTransmission = false; + return 0; +} +uint8_t TwoWire::endTransmission(void) { + Assert(inTransmission, "Should be in a transmission"); + inTransmission = false; + return 0; +} + +size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { + auto available = receivedData.size(); + if (available > size) { + available = size; + } + return available; +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop)); +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop != 0)); +} +size_t TwoWire::requestFrom(uint8_t address, size_t len, bool stopBit) { + return uint8_t(requestFrom(uint16_t(address), size_t(len), stopBit)); +} +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size) { + return uint8_t(requestFrom(address, size_t(size), false)); +} +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { + return uint8_t(requestFrom(address, size_t(size), sendStop != 0)); +} +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t size) { + return uint8_t(requestFrom(address, size_t(size), false)); +} +uint8_t TwoWire::requestFrom(int address, int size, int sendStop) { + return uint8_t(requestFrom(uint16_t(address), size_t(size), sendStop != 0)); +} +uint8_t TwoWire::requestFrom(int address, int size) { + return uint8_t(requestFrom(uint16_t(address), size_t(size), false)); +} + +size_t TwoWire::write(uint8_t ch) { + Assert(inTransmission, "Should be in a transmission"); + sentData.push_back(ch); + return 0; +} + +size_t TwoWire::write(const uint8_t* buf, size_t size) { + for (size_t i = 0; i < size; ++i) { + write(buf[i]); + } + return size; +} +int TwoWire::available(void) { + return receivedData.size(); +} +int TwoWire::read(void) { + if (receivedData.size()) { + auto result = receivedData[0]; + receivedData.erase(receivedData.begin()); + return result; + } else { + return -1; + } +} +int TwoWire::peek(void) { + if (receivedData.size()) { + auto result = receivedData[0]; + return result; + } else { + return -1; + } +} +void TwoWire::flush(void) {} + +extern TwoWire Wire; +extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index 98cddae8a..1bda1ea24 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -1,50 +1,64 @@ #pragma once #include +#include #include "esp32-hal-i2c.h" #include "Stream.h" +#include "../FluidNC/test/TestFramework.h" class TwoWire : public Stream { + bool inTransmission = false; + std::vector receivedData; + std::vector sentData; + public: - TwoWire(uint8_t bus_num) {} - ~TwoWire() {} - - //call setPins() first, so that begin() can be called without arguments from libraries - bool setPins(int sda, int scl) {} - - bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } // returns true, if successful init of i2c bus - bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0) { return true; } - bool end() { return true; } - - void setTimeOut(uint16_t timeOutMillis) {} // default timeout of i2c transactions is 50ms - uint16_t getTimeOut() { return 0; } - - bool setClock(uint32_t) {} - uint32_t getClock() { return 0; } - - void beginTransmission(uint16_t address) {} - void beginTransmission(uint8_t address) {} - void beginTransmission(int address) {} - - uint8_t endTransmission(bool sendStop) { return 0; } - uint8_t endTransmission(void) { return 0; } - - size_t requestFrom(uint16_t address, size_t size, bool sendStop) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop) { return 0; } - size_t requestFrom(uint8_t address, size_t len, bool stopBit) { return 0; } - uint8_t requestFrom(uint16_t address, uint8_t size) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t size) { return 0; } - uint8_t requestFrom(int address, int size, int sendStop) { return 0; } - uint8_t requestFrom(int address, int size) { return 0; } - - size_t write(uint8_t) { return 0; } - size_t write(const uint8_t*, size_t) { return 0; } - int available(void) { return 0; } - int read(void) { return 0; } - int peek(void) { return 0; } - void flush(void) {} + TwoWire(uint8_t bus_num); + ~TwoWire(); + + // For unit tests: + void Send(std::vector data); + void Send(uint8_t value); + std::vector Receive(); + void Clear(); + + // TwoWire interface: + + // call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl); + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 0); // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda = -1, int scl = -1, uint32_t frequency = 0); + bool end(); + + void setTimeOut(uint16_t timeOutMillis); // default timeout of i2c transactions is 50ms + uint16_t getTimeOut(); + + bool setClock(uint32_t); + uint32_t getClock(); + + void beginTransmission(uint16_t address); + void beginTransmission(uint8_t address); + void beginTransmission(int address); + + uint8_t endTransmission(bool sendStop); + uint8_t endTransmission(void); + + size_t requestFrom(uint16_t address, size_t size, bool sendStop); + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); + uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop); + size_t requestFrom(uint8_t address, size_t len, bool stopBit); + uint8_t requestFrom(uint16_t address, uint8_t size); + uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop); + uint8_t requestFrom(uint8_t address, uint8_t size); + uint8_t requestFrom(int address, int size, int sendStop); + uint8_t requestFrom(int address, int size); + + size_t write(uint8_t ch); + size_t write(const uint8_t* buf, size_t size); + int available(void); + int read(void); + int peek(void); + void flush(void); inline size_t write(const char* s) { return write((uint8_t*)s, strlen(s)); } inline size_t write(unsigned long n) { return write((uint8_t)n); } diff --git a/X86TestSupport/TestSupport/freertos/Task.cpp b/X86TestSupport/TestSupport/freertos/Task.cpp index 8dd60b5c6..05036f34c 100644 --- a/X86TestSupport/TestSupport/freertos/Task.cpp +++ b/X86TestSupport/TestSupport/freertos/Task.cpp @@ -27,6 +27,7 @@ BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode, void vTaskDelay(const TickType_t xTicksToDelay) { Capture::instance().wait(xTicksToDelay); + delay(xTicksToDelay); } void vTaskDelayUntil(TickType_t* const pxPreviousWakeTime, const TickType_t xTimeIncrement) { From b36b5c8dfc73d86c39652546d1bc38c594059efa Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 14:46:09 +0100 Subject: [PATCH 083/100] Fixed a few bugs. According to the tests this should pretty much work. --- CodeCoverage.cov | 672 +++++++++++++++++++++ FluidNC/src/Extenders/I2CExtender.cpp | 71 ++- FluidNC/src/Extenders/I2CExtender.h | 8 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 474 ++++++++++++++- FluidNC/test/Pins/GPIO.cpp | 55 +- X86TestSupport/TestSupport/SoftwareGPIO.h | 2 +- X86TestSupport/TestSupport/Wire.cpp | 29 +- X86TestSupport/TestSupport/Wire.h | 9 + 8 files changed, 1251 insertions(+), 69 deletions(-) create mode 100644 CodeCoverage.cov diff --git a/CodeCoverage.cov b/CodeCoverage.cov new file mode 100644 index 000000000..a3f4cc636 --- /dev/null +++ b/CodeCoverage.cov @@ -0,0 +1,672 @@ +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.cpp +RES: _______cccccc_uuuuuu_uuuuuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_ccccc_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___ccccccccccccccc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\LegacySettingRegistry.h +RES: ____________uuuu______________u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\FS.h +RES: ________________________________________u________u_____________________c___________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10vSpindle.cpp +RES: ______________________uu__uuu__u_uuuu_uu_____u_u_u_uu__uu__u___uuuu_uuu__u__uuuu_u_uuuuu_uuuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StandardStepper.cpp +RES: _______________________u_uuuu_uu_uuuu_u_uu_uu_u_uuuuuuuuuuu_uuuu_uuu__uuuuuuuuuuu__uu_uuu_uuuuuu_u_uuu_u_u_u___c__uuuuuuu__uuuu___u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.h +RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________ccccuuuu__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Authentication.cpp +RES: ___________________________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.cpp +RES: ____cc_c__ccccccccccccccc_cccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.cpp +RES: _______uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPI.h +RES: ____uu___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\InputBuffer.h +RES: ____________u__________u_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SDFS.cpp +RES: ____cuuuuuuuuu_c__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.h +RES: ___________c______________c_c_c_________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.cpp +RES: ______________________________u_u_uuu_uuu__u_u_uuu_uuu___u_uuuu_uu_uu_uuu_____uuuuuu__uuu___uuuuuuuu_uuu_________________________________________________________________________________________uuu___uuu__u_uu___u_uuuu_uuu_uu_uu__uuu___uuuu_u_u_uu___u_uuuuuuuu__uuu_uu_uuuu_u______uuuuuu_uuuuu_____uuu_uuuuu_uuuuu_uuuuuuuuu_uuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPIFFS.cpp +RES: ____cu___uuuuuuu__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.cpp +RES: ____________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.cpp +RES: ________c_u____________cccuucc___u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.cpp +RES: ______________________________________ccccccc_u_u__uuuuuuu_uuuuu_u_uuuu_uu_uuu_ccc_uuu_uuu_ccc_ccc_cccuu_cc_ccuuc_c_uuuuu_uu_uuuuu_u_uuu_uuu_uuuu__uuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___c_c_c__cu___cc_cc_cc_u_u_u__uu___uuu_uu_uu_uu_uu_uu_uu_uu___uuu___uuuu_u__uuu__uu___uuuuuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.h +RES: ___________________________________________________________________________________c______c______c________u__cccc__uu_cc___cccc____c_c_u_uu_c____c_c__u_c___u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\rmt.cpp +RES: ____uuu_u_uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.h +RES: __________________________________________cc____ccu_cc_u______u_______________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\SPIBus.cpp +RES: __________uuuuu_u_uuu_uuu____uuuuu_u_uuuuu__u_________u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\nvs.cpp +RES: __uuuuuuuuu_uuuuuu_uuuuuu_uuuuuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\BTConfig.h +RES: __________uuu_u__________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\nvs.h +RES: ____________uuuuuuu_u_uuuuuuu_u_uuuuuuu_uuuu_uuu_u_uuuuuuu_uuuu_uuu_u_u_uuuu_u_u_uuuu__________uuuuuuu______________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Report.cpp +RES: ________________________________________________uuu_u_u__uuuuuuuu__uuuuu_u________uuu_uu____uuuuuuu__uuu_uu________uu____u_uu_u_u_uu_uuu__u_u_u_c______________c______uuuu_u___uu__________u__uu____u___uuuuu__u_uuuuu_uuu_uuuu_uuu_u__uuu_u_uu_uu_uu_uu_uu_uu_uu_uu_u__u_u_u_uu_uu_u__u_u_uu_u__u_u_uu_u__u_________u_uu_u__u__u_uu_uu_uu_uu_u__u_u_uu_uu_uu_u_u__uuuu_uu_uu___uu__uuuuuu__uuuuu_uuu______uuu_uu_uu___u_u_uuu_uuu_u___uuu______uuuuu_u_uu_uuu_uuu_uuuuuuu_uu_u_u_uu__u_u__u_u_uu_uu__u_u_uu_uuuuuu_u__uuuuuuuu_u_u__uuuuu_u_u______uu__uuuuuu_u___uu__u_uuuuu_____uuu_u_u_uuuu_____u_u__uu_uu__uuuu_____u_u___uuuuuu_u_uu_u_____u_uu_uu___uu_______uu_u__uuuuu_uu_u_______u_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\I2SO.cpp +RES: ____uuu_u_u_uuu_uuu_u_uuu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\FS.cpp +RES: _________________________uuu__uu_uuu__uu_uuu__uu_uuu__uu_uuu___uu__uu_uuu__uu_uuu__uuuuu_uuu__uu_uuu__uu_uuu__uu_uuu__uu_uuuu_u_u_uuu__uu_uuu__uu__uuu_uu_uuu_uu_uuu_uu_u_uuu__uu_uuu_uu_u_uuu_uu_u_uuu_uu_u_uuu_uu_u_uuu_uu_u_u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\LegacySettingRegistry.cpp +RES: ________u_u_uuu_uu_uuuu_uu_uuu_uuu_u_uuuuuu__u_uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Task.cpp +RES: _____________c_______ccccc_cccp_uuu_uuuuu_uuu_uuu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Capture.h +RES: _______________________c__cccc___uuuuuuuu__________________________ccuuu_u_c_____uuuu_____________uuuuuu_u_uu_uuuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spindle.h +RES: ______________________________uu____________uu__u___u__uu___uu_u_______u_u___uuuu_uu_u__u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Queue.cpp +RES: ______uuuuuuu_uu_uu_uuu_u_uuu_u____uu_uuu_uu_uuuu_u_uu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\ExceptionHelper.cpp +RES: _______________________________________cccc__cc__ccu__cccc_______c_cc____ccc_ccccccccccccccc__ccccccc_c__cu__cccccccc__ccc_c_________ccccccc_cccc_c__cccc_ccuc_uc_c__ccccuc_cc__cu__cu_c_u_uc___uuuuuu_u__u___u______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\esp32-hal-timer.cpp +RES: _________uuuu_uuu_uuu_uu___uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\LimitPin.h +RES: _____________u_____uu___u_________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.cpp +RES: __________c_ccu_uuuu_u_uuu___uu_u__uuu_uuuuu_u_uuu_u_c_c_u_u_uu_u_uu_uuuu_c_cuucuuuu_ccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\GPIO.cpp +RES: _____________________________uu__uu__u_u__u_________pu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu_uu_uu_uu_uuuuuuu______________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu_p_p_p_pu_uu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uu_uuuuu_u_uuuu_u_uuuuu_pu_uuu_pu_uuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu______uu_uu_uu_uu_____________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu__p_p_p_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\LedcPin.cpp +RES: ______________________u________uuuu_uu_u_u_u____uuuuuu_uuu____uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StandardStepper.h +RES: _________________u__________________uuuuu__u_____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10vSpindle.h +RES: ___________________________________u_uuuuu__u_u_________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-internal.h +RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________p____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StepStick.cpp +RES: __________u_uuu_uu__u_uu_uuuuu_uuu__uu_u__u___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Regex.cpp +RES: _____________________u_uu_uuu__uuu_uu_uu_uu_uu___uuu__uu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\IPAddress.h +RES: ____________________uccccccu_u_uu___u_________________u__c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\GenericFactory.h +RES: ____________cccc__________c_____u______c_____c_u__uuuuuu_u_uuu_uuuuuuuu_u_uuuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\xmodem.cpp +RES: ________________________________________u_uuuuuu______________________u_uuuuu_____________uuuuuuu_uuuuuu__uu_uuuu______________uu_u_uuu_uuu_uuuuuu_uuuuuuuu_u___uuuuu_u__uuuuu_uu_uu_uuuuu_uuuuu______uuuu_uuuuuu__uuuuuuuuuu_uuuuu_uuuuuuu_uu__uuuuu_uu__uuuu___uuu_uu_uu_uuuu______uuuuuu____uu____uu_uuuuuuuuuuuuuuuu_uuuuuu_uuu_uuuu_______uuuuuuuuuuuuuu_uuu_______________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Channel.h +RES: ______________________________c__c__u__u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.h +RES: ____________________________cccc________________uuu______u__uuuuu______uuu_u_u_uuuuu_uuu_______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spindle.cpp +RES: ________________u_uuuu_uuuuu_u_uuuuu_u__uu_uuu_uuuu_________uuu_uuuu__uuu__uuuuu_uuuu_u_uuuuu_uuuuuuu_uu_uuu_uuuu_uu_u_uuu_uu_______uu___uuuuu___u_u_uu_u__u___u_u__uu_uuuu_u_uu___u_u__uu_uuu_uuuu____uu_uu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.cpp +RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__cuuuuuu_uuuuuuuuu_uu_uc__cc_u_c_uuu_uuuuuuuu_c_____c__c___c__c_ccuu___cucu___cu__cc_cccc_cccc_cccu_cu_cu__cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.cpp +RES: ____________uu_uu__u__uu_u_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\RcServo.h +RES: ________________u_____uu_uu_u_u______________uuuuuu__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\DebugPinDetail.cpp +RES: ___________u___uuuuuuuuu__uuuuu__uu_uuuu_uuu_uuu_uu_uu_uu_uu_uu_uu_u_uu_uu_u_uuuu_uu__uuuu_uu_uuu_u_u_uuuuuuuuuuuuuuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\VFDSpindle.cpp +RES: _________________________________________u____uu________________u__u___uu__u_uuu__uuu_u__u_uu_u___u_uu_uuu_u__u_____uuuu_uuuu___uuuu____uu_________uu_______u__uuuu_________uu_uuu__uuu___uu__u_uuu__u___u___uu__uu_uu_u_u__uuu__uu___u______u_uuuuu_uuuu__uu___uuu___uu__u_uuu_____u_u__uuu_________u_uu____u_u_uuuu__u_uu_u_u_uu_u_u__uuu____u_uu_uuu_u__________u__uuuu_____uuuu__u_uu__u_u_u_uuu___uuu_uuu_uuuuu__u_uuu__u_u_uuuuu__u_uu_uuuuu__u_uuuu_u_____u___u_uu_____uuuu_uuuuuu_uu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\MotorDriver.cpp +RES: ____________________________uuu_u_u_uuuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.h +RES: ______________________________________u____u__uu__uc____________cc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.cpp +RES: _____________u_uuu_uu_uuuuu____uu__u_uu_uuuu_u_u_____________uu_u_uu__uuuu_uuuuu_uuu_u_uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.h +RES: ____________________u_______uuuuuuuuu_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\RelaySpindle.cpp +RES: _______________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SDCard.h +RES: ________________________________________________________________________uuuu___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\NullSpindle.cpp +RES: _________________uuuuuuuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\uartdriver.cpp +RES: ________uuu_uu_uuuuuu_uuu_uuuuuu_uuuuuuuuuuu_uuuuuuuuuuuuuuuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\RelaySpindle.h +RES: ______________________u____u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SDCard.cpp +RES: _____________u_______uu_u_u__u_u___uuu___uu__uu___u_u_u__uuu__uu_uuu_uuu_uu_uuu_uuuu_u__uuuuuuu_u___uuu_u___u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WifiServices.cpp +RES: _________c_cu_uuu____________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\YL620Spindle.cpp +RES: ____________________________________________________________________________u_uuu__uuu_uu_uu_uu_u__u_u____uu_uuuuuu_uuuu_uuuuu___uuu_____uuuuu_uuuuu___uuu____u_uu_u__uu________uuuu_u_uuu__uuuuu___uu_u_uuuu_uuu__uuuuu_____uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\YL620Spindle.h +RES: __________uu_______u_uu__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\CoolantControl.cpp +RES: ______u__uuuu__uu_uu__uu_uu_uu___uu_uu___uu_uuuu__uuu_u___uuuu______uuu_uuu_uuuu_uuuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\FreeRTOS.h +RES: ____________________________uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Serial.cpp +RES: ________________________________________________________________u_uuuuuu_u_____uu__uu_uuu_uu_uu_uu_uu_uu_u____u_uu_uu_uuu_u_uuu_u_uuu_u_uuu_u_uu_uu_uu_uu_uu_uuu_u_uuu_u_uuu_u_uuu_u_uu_u__________u__uuu_uu____u_uuuuu_uuuuuu_uuuuuuuu_uuuuuuuuuuuuu____u_uuu_u_uu_uuu_c_u_____uu_uuu_u_u_uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Serial.h +RES: ________________________________________________________________________c_________uuuu___u_______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ControlPin.h +RES: _____________u___uuu____u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Control.cpp +RES: ________uuu_uuuuuuuuuu_uuuuuuuuuu_uu_u_uu_u__u___uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\dac.cpp +RES: ______uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.h +RES: _________________________u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\LimitPin.cpp +RES: _____________uu_u_uuuu_uuuu_uuuu_uuu_____uuuu_uuuu___________uu__u_uuuuu_uu_uuu_uu__u_uuu_uuuuu_uu_uu____uuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.h +RES: ____________________________________c___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinCapabilities.h +RES: __________________c_____________________________cccc___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.cpp +RES: _____u_u_uu_u_u_u_uu_u_uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Probe.cpp +RES: _________u__uu_uuu__u_uuu__uuu____uuu_u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Laser.cpp +RES: _______________uuu_uu_u__u_u_u_uu_u_u__uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Laser.h +RES: ____________________________uu_u_u___uuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Generator.cpp +RES: ____________uuu_uuuuu_uuu_u_uuuu__uu_uuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\MyIOStream.h +RES: _________uuuu_cccc_cccc_cccc_cccc______uuuu_uuuu_uuuu_____u_u_________u__uuu_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Generator.h +RES: ____________________u_uuuuuu_______uu____uuuu_uuuu_uuuuuuuuuuu_uu_uuuu_uu_uu_u__u_uu_uu_u__uu_uuuu_uuuuu_uuuuuuuuuuuuuuu_uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\ledc.cpp +RES: _________cuuuuu______u__c_uuuuuuuuuuuuuuuuuu_uu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Logging.cpp +RES: ___________ccc_______ccc_cccc_ccc +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Jog.cpp +RES: _____________u__uuuu_u__uu__uuuuu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StackTrace\AssertionFailed.cpp +RES: __________________________________________c_cccc___ccccc_cc__cc__c___u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Planner.cpp +RES: ________________________uuu_uu____________uuuu_uu__uuu_uuu__________________________________________________________________u_u_uu______u_uuu_uu_uuuuu_uu__uuuuuu__u___uuuuu___uu_uuu______uu_uuu_uuuu_uuuuuu__uuu_uu_u_u__uuu__uuu_uu_uuuu_uu__uuu___uuuuuuu_uu__uu_uu___u_uuuu__uu_u__uu__uuuuuuuuuu____u_uuuuuuu_____uuuu_uu___u____uuuu_uu_u____uu______uuu_uuuuuu___u__uuu______________________uuuuu_u_uuu_uuuuuu______uuuu_uu_uu_u_uu__u__uuuuuu___uuuuu_u___u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.h +RES: _______________c__uuuu_u_u_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WifiConfig.h +RES: __________________uuuuuuu_uu_______________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\VFDSpindle.h +RES: _________________________uuu_____________________________________uuu_u__uu______u______________uuuu_uuu_uu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.cpp +RES: ____________________________uuuu__u__uuu_u_u____u__uu_uu_____u_uuu_uuu___uu__u_____u_uu__uu_u___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\WebSettings.cpp +RES: ______________________________uu_uuuuu_______uuuuuu__uu_uu__u_uuuu_uu_u_uuuu__uuuuuuuu_u_u_uuu__uuu_uuu_uu__uu_uuu___u_u__uuu_uuuuu_uuuu_uuuuu_______________uuuuu_uuuu_uuuuuu_uuuuuuu__u_u_uuu_uuu_u__uu_uuuuu_uuu_uuuu__uuuu__uuu_uuu_uu_uuuu_uuu__uuuuu_uuu__uu___uuuuu_uuuu_uuuuuu_u_uuu_uuu_uu_uuuu_uuu_uuu_uuuuu_uuu_uuuu_uuuu_uuuuu_u_uuu_uuuuu_uuuu_uuu_uuuuu_uuu_uuuuu_uu_uuu_uuuuuuuuu_uu_u_uu_uu__uuuu_uuuu_uuuuuuuuuuuuuuuu_uuuu_uuuuuuuuuu_uuu_uuu_uu_u_uuu_uuu_uuu_uuuuu_uuu___uu__uu_uuu_uuuuuuuuu_uuuuuuuuuuu_u_uuu_u_____________________u_uu____uuuu_uuuuuuu_uuuuu_u_uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Settings.h +RES: _______________________________________________________________________uuuuu_________u______u________u_________u___u_uu__u_________uu_uuuu_u________uu___u____u__________________________________________u________u_u_u_u_uu_u_u____________________________________u__c______________________________________u____________________________________u_______________uu______________uu___________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\JSONEncoder.h +RES: _______________________________________u___________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Kinematics.h +RES: ___________________________________u______________u___________________uuu_____u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Macros.h +RES: ______________________uuu_uuu____uu_uu_uuu_uuu___uuu_____uuuuuuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Dynamixel2.cpp +RES: ______________________________________uuu_uuuuu_uu___uu_u_u_uuu__uu__uuu_uu_u_uu_u_uu_uuuuuu_uuu__uu_u__uu_uu__u_uu_uuuu_uuu__u___uu_u_uuuuu_uuu__u_u___uuu__uu_uuuu_uu_u_____u_uu_u_u_uu__u_uu_u_u_u_uuu_u_uu_uuuuu_uu_uu_uuuuu__uuuu_uuuu_u____u_uuuuu_u_uu_uuu_uu_uu_uu_uu_uu_uu_u____u_u_u_u___u_uu__u__u_uuuuuu_u__________uu_uuu__uu_uu___u_uu_uuu__u__uuuuuuuuuuuuuuuu__uuuu_uu___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinDetail.cpp +RES: _______uu_uuuu_uu_uu_u__uuuu_uuuuu_uuuu_u___uuuu_u_u_uuuuuu__u__uuu_u_uuuuuuuu_uuuu_uu_uuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\SPIBus.h +RES: ___________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\InputBuffer.cpp +RES: _______c_c_uuuu_uuuu_u_u_u_uuuuu_uuuuuuu_uuu_uuuuu_uuuuuu_uuuuuu_uuuu_u_u__u_uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\MachineConfig.cpp +RES: _________________________________uuuu_uuuuuuuuuuuuu_u_uuuuuuu_uu_uuuu__uu__uu__uu__uu__uu__uu__uu_____uu__uu__uu____u_uuuu_u_uu_u___u___uuuuuu__uuu__uu_uuu_uuuu__uuuuuu_uuuu_uuuu_uuuuu____uuu_u_u_u_u_uuuuu_u_uuuuu___u_uu__uuu_u_uuu_uuu_uuu_u_uu_cccccccccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepping.h +RES: _______________________u________________________u_uuuu_u__________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\NullSpindle.h +RES: ______________________________u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\HandlerBase.h +RES: _______________________________________uuuuu_uuuuu_______________pp_ppp_puu__p__uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StringRange.h +RES: ______________u_c_c____u__________uuuu_uuu_uu__uuuuuuuuuu__uu_uuuu_uu_uu______cccccc_c__cu_c_ccuucccccc_c_u_uuu_u_uuu_u_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\CoolantControl.h +RES: _____________u______uu___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Probe.h +RES: ___________________u______u______u___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\MachineConfig.h +RES: __________________________________uu_____u__u_uuuuu__u_____ccccccccccccc_c_cccc_c_____c__c_ccc_u_uu______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\MotionControl.cpp +RES: ______________________________uuu______________uu_u__uuu_________________uu___uuuuu_u__uuu_uuu_uuu_u____uuuu________________uuuuuuu_u_uuuu__uuuu_uuu_______u_u___uuu_u_uuuu__________________________uuu____uuuu_uuuuu__uuuuu__uuuuuuuuuuu_uu_u__uu__uuu_uuu____uu__u_____uu___u_uuu____uu_uu_______uuuu__uu__uuu__u__uuuu__uuuuu__uu_u_u_uuuuu_u_u___uuuuu_uu_uu_uuuu_u_uuuu_u___uuu_uuuuuu_uuu_uuuuu_u_u_uuu_uu_______u_uu_____u_uuu_uu_u_u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\JSONEncoder.cpp +RES: ________u________u______uuuu_uu____uuu_uu__uuuuu_____uuu_uu__u__uuuuuu_u__u___uuuu_u__uuuuu__uuuuuu__uuuuu___uuuuu__uuuuu__uuuu__uuuu__u___uuu_____uuuuu___uuu___uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Dynamixel2.h +RES: _________________________________________________u_____________________________________uu_____u_________uuuuu_uu_uu_u_u_uuuu__u_u__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinDetail.h +RES: _______________u___________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Main.cpp +RES: _____________________________________uuuu_uuu_uuu_u_uu_uuuuu_uuu_u___uuu___uuu___u_u____________________________________________u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.cpp +RES: _______cu_u_uu_uuuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.h +RES: ________________________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Limits.cpp +RES: __________________u________________u_____uuu_uuuuu_uu____uuu_uuuuuu_uu__uu____uu_uu_uuuuuuu_uu___uu_uuuu_u_uuuu_u_______________________uuuuu__uu_uuuuu__uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinCapabilities.cpp +RES: ______c__________ccccc_cccc_cccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Protocol.cpp +RES: __________________________________c_____________c_________________________________________uuu_uuuu_u_uuuuuuuuuuuuuuuu___u________u___uuu_uuuu___uuuu_uuuu__u_________uu_uuu__uuuuuu_u_uuuuu_u_uu___uu_uu____u_uu__uuuu_____________uuu_u_u___u__uuuuu_uu_______uuu_u____________uuuu_u_uuuu____uu_u___uuuu______uuu_uu__uu_uuuuu_u_uuu_u_uuuu_u__uu____u___u__uu__uu__uu_u_______uu_u_uuu_____u___u__uu__uu__uuu_uu_u____uuu_u___uu__u_uu_u_uuuuuu__uuuu_u_uu_uu_uu__u_uu___uu_uuu__uuu__uu___u__________uu_u_uu_u_uuuuuuuuu_u___uu____u_uuuu_____u__u_uu__uuuuu____________u_uu_uu_u_uu_u_uu___uu__uu__u_uu_u_u________u__uuu_uuu___________uuuuuu_uuuuuuu___u_u_uuuuuu___uuuu___uuu___uu_uuuuu______uuuuuu__uu_uuuu__u___u_uuu__u__u_u__u_uu_u_u_uuu__uu_uu_____________________________uuu_u_uu__uuu__uu__uu__uu__uu__uu__uu__uuu_uuu_uuu_uuu__u________u_____u_____u__u______u__u_uuuuuu_u___uuuuuuuu_uu__uuu____uu__u__u_uu_u__uuuu____u__uuuuuuu__uuuuuuuu_uuuu_u__uuu__uuuuu_uuuuuuuu__uuu___u__u_uuuu___u_uu_uuuu___u_uuu____u_u___uuuuu__uuu___u__u_uuuuuuuuu_uuuu_uuuu__uu_u_u__uuu____uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axes.h +RES: _______________u___u_____________u____u_______uuu_uuuu_uuuu_______________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\lineedit.cpp +RES: _____ccc___uuu_uuuuuuuu_u_uuuuuuuuu_uuuuuu_u_u_uuuuuuuuuuuu_u_uuuuuuu_u___uu_uuuuu__uuuuu_u____uu_uuuuu__uuuuuuuu_uuuuu_u_uuu_uuuu_uuu_u___uuuu__uu_uu_uu_uuu_u___u____u_uu_uu_uuuuuu_uuuu_uu_uu_uuuu_u_uuuu_u_uuu_u_uuuu_uuuu_uuuuu_uuuuuuuuuuuuuu_uuu___uuuu_uuuu_______u__uuu__uuu_uuuuu_uuuuuuu___uuuuuuuuuuuuuu_uuu_uuu_uuu__uuu_uu_uuuuuuu__uuuu_uuu_uuuuuuu_uuu__uuuu_uuuuuuuuuuuu_uuuuuuuuuu__ccccccc_uuu_uuuuuuuu_u___uuuuuu________uuu_u__u_u_uu_uu____uu_u__uu__uuuuu_uu_uuu___uuu________uuu_u_uu_uu_u__uu___uuu__uu_uu_uu___uu_uu_uu_uu_uu_uu_uu_u______uuuuuuu____uuuuu_u_uuu___u_uu__uu_u__uuu__uuu__uu__uuu_u__uuu__uu_uu_uu_uu__uuu__uuu_uuuuu__uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\lineedit.h +RES: ______________________________c__________c___c_c__c_______________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Parser.cpp +RES: ______________c_u_uuuu_u_ccc_ccc_ccc_cc_c_uuuu_uu_uu__uu_uuu_uu_uu_uu_uu_uuu_uuuuu_uuuuu_uuuuuu_uuuu_uuuuu_uu_uuuuu_uuu_uuu_uuuu_u_u__uu__uu__uu_u___uuuuuuuuu__uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\WallPlotter.cpp +RES: _____________________uuuu_uuu_uu_uu______uuuuu_u_uu_u_u___________u_____uuu___uuuuu__u_u__u_uu___uu_uuu____u_______________uuu__uuu_u__uu_______u___u__uuu__u_______________________uuu__uu_uu__uuu__uuu_u__uuu_uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\MotorDriver.h +RES: ______________________________________u____________u_____________________________u__________u_____u__u______u___________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\WallPlotter.h +RES: ________________________________u_u__u_u_____________uuu_uuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Midtbot.cpp +RES: _______u_uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\H2ASpindle.h +RES: ________________u_uu__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Midtbot.h +RES: _______________________________u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\PWMSpindle.cpp +RES: _____________________uuu_uuu__uuu__uu_u_u_uu_u_u_uuu__u__u_uuu_uuuu__uuu____uuuu_u__u_______uuuuu__uuu__uu___u_uuu___uu__u_uu______uuu____uuuuuu_uuu_uuuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homing.cpp +RES: ________________________________________uuuuu_uuu_u_uuu___u__u_uu_u_u___u_uuuu_uuuuuuu_______u_uuuu_u___uuuuu_u__uuuuuuuuu_uu_uuuu_uu_u__uu__u_uu__u__u_uu__u_____u_u_u_u_uuu_u__u_u__u_uu_uuu___uuu_u__uuuuu__uu_u__uu___uuu_u__uuuuu___uu_u_uu___________u________uu__uuu_uuuu_uuu_uu_uuuuu_uuuu_u_uuuuuu__u___uu_uuuuuu__uuuu_uuu_uu__u__uuu_u_u__u___________uuu_uuuu__u_uuuuuuuu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.h +RES: __________________________________u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Commands.cpp +RES: __________________________uuuu_uuu_uuuu___uu___uuuu_uuu____u____uu_uuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\RcServo.cpp +RES: _________________________________uuuuu__u_uu_uu_u_uu_uuu_u_uu__uuu__u_uu_uuu_u__u_uu_uuu_u_uuu_uu_u_uuu_____u_uu__u____uu_uu_uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\PwmPin.cpp +RES: _____________u_____uu_____________uu___uuu_uu_uu__uu_uu__u__uuu_u__uuuuu_u__u_____uu__uu_uu_u_uu____uuu_uu_uuuuu__uuu_uu_uu__uuuu_uu__uu__uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\PwmPin.h +RES: _________________u_____________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Arduino.cpp +RES: ________uuu_uu_uuuu__u_ccc_ccc_uuuu_cccc_uuuu_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.h +RES: ________________________________uuu__u__u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SoftwareGPIO.h +RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_uu_u_cc_c_uu___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cuuuu__c_cc_cuuuuuu_uc_c_u_ccc_cccc_ccccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\PinOptionParsing.cpp +RES: _______puu__uuu___uuu_uuuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuuuuu_uuuuuu_uu___uuuuuuuu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\TestFactory.cpp +RES: ______________________________________uu__uu____uu__uu_uuuu_u_uuu__u_u_uuu_u_uu_uu_uuuu_uu_uu_uu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\Undefined.cpp +RES: ______p__uu__uuu___uuu__uu_uuuu_p_uu_uu__uu_uu__uu_uu__uu_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\ErrorPinTest.cpp +RES: ______c__c_cc_c_cc_pu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Extender\I2CExtenderTests.cpp +RES: ________________u_____u_c_c_____c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__c_cccccc__ccc__cccccc_c_cccccc__ccc__ccccc_cccccccccccccc_____c_cccccccc__c_cccccc__ccc_c__ccccc___________________c_cc__c_cc_uuuu___uuuu_uuuu____uuuu_uuuu__uuuu_uuuu__uu____uuuu_uuuu________u_uu__u_uu_uuuu______u_uu__u_uu_uuuu___uuuu_uuuu___uuuu_uuuuu_cc__cccccc__ccc_c__ccccc___________________c_ccc__cc_cccc___cc_cc_cc____cccc_ccccc__cccc_ccccc__cc__c___cccc_ccc_cc________c_cc__c_cc_ccc_cc______c_cc__c_cc_ccc_cc___ccc____ccc____cccccccc___ccc____ccc_c_c__ccccccccccc_cucccccu_cu_c_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c__cc_c__c_cc_ccccc_c___ccc____cc__ccc_ccuuu__u__uuuuu_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c___ccc_c____cc____cc__c__cc_c__c__cc__cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.h +RES: ______________c____c__c_____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-port.h +RES: ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________c___________cccc_c_c__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlTreeBuilder.cpp +RES: ________________uccccc_______ucccc__________cc_______uccccc________uccccc____ccc_c____c__cc____cc_cuuu_cc___cc_cuuu_cc__cc_cuu_cc__cc_cuu_cc_________cc__ccuu___uuu_uu_cc_________cc__ccuu___uuu_uu_cc_________cc__ccuu___uuu_uu__c_ccccu__uuuuu_uuuuu_uuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\NutsBolts.h +RES: ____________________________________________________________________________________________________________uuu____uuu_uuu_____uu___uu____uuu__uuu_uu_uu__uuuu___uuuuu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\ParserHandler.h +RES: _______________________cc___c______cc_______c__c____cc_c___cc__u__uu_cc_______c_________c____cc_c__c_ccuu_c_ccu_c_uuu_u_uuuu_u_uuu_u_uuu_u_ccc_c_uuuuuu_uuu_u_c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axes.cpp +RES: _________________uuuuu_uu_uuu__uuuu__u__uuuuu_u_uu_uuuuu_uu_uuuu_uu___uuu_uuuuuuuu_u__u_uu_uuu_uu_____uu_uu_uuuu_uuu___uuu_uuuu__uuuu___uuu__uuuuuuuu_uu_uu_uuuuu___uuuuu_uu_uuu_uuuuuuu_u_u_uuu___uuu____u____uuuu_uuu_u_uuuu_u_uu__uuu_uu_uuuu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StackTrace\AssertionFailed.h +RES: _____________________________c__u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlParser.cpp +RES: ______cc____cc_cc__c_cc__c_cc_ccc_cc_____cc_cc__c_c_ccc___c_cc_ccc_cc_______cc_cc__c_cc_cc___c_cc_cc___c_cc_ccc_cc__________cc_cc__c_cc__cc_cc_c_cc___c_cc_cc_c_cc____c_cc_ccc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\H2ASpindle.cpp +RES: ____________________u_uuu_uuuuuu_u__uu_uuuuuu_uuuu__uuuuu___uu_uu__uu_u_uuuu_u_uuu__uuuuu___uuuuu_uuu__uuuuu_____uu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\gtest.h +RES: __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________c_______c____________________________________________________________________________u__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Configuration\YamlComplete.cpp +RES: ______cc_____________________________________cc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_ccc_cc_c_cccccuuuuuuuuu_uu_u_u_uuuuu_uu_u_uuu_uuu_uuu_uuuuuu__uu_u_uuu_uu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Tokenizer.h +RES: ________________ccc_cc_cccc_c_ccu_ccc_c_c_cccc_c_cccuuu_cccc__________c_____________c_____c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\NotificationsService.h +RES: ____________u__________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.cpp +RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccu_u_u_u________c__cc______cu__c_u__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccu_uuu_ccc_c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.h +RES: ________________________________u_______uu_______________u_uu_uuu_u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\GCode.cpp +RES: ________________________uuuu_____________u_u__uuuu___uuu_u_uuuuuuuuu_u___u_u__u_uuu_u_u_uuu__u__uu___uu_______u__u_uu__u_u_u_uu_uuu_uu_______u_u__________uuu_u_uu_uuuu____u_uuuuu_________u___uuuuuu_u_uuu_uuu________uu___u____u__uuuu_u_uu__uu_uu_u___u_uu_uu_uu_uu___uu_uuu_uuu___uuuu_uuuu_uuuu_uuuu__uuu___uu_uu_uu_uu_uu_uu_u__uuu__uuu_uuu_uuu_uuu_u_uuu_u__u_u__u_u_uuu_u__uu_u__u_uuu_uuu_uuu_uuu____uu_____uu__uuuuuuu_uuu_uuu_uuu_uuu_uuu_uuu_uuu__uu__uu_u_uu___uuu_uu__uu_u__uuu__uu__uuu__uuu___u_uu_uuuu_u_u__uu_u_uu___u_uu_u_uu_u_uu___uu_uuuuu_u_uuu_uuu_uuu_uuu_uuu_uuu_u___uuu_uu______u_uuuuuu_u_uuuuuu_u_uuuuuu_u__uu_u_uuu__uuuu_uuuu_uuuu_uuu_uuu_uuu_uu_u_uuu_uuu_uuu_uuu_uuuu_uu_u_uuuuuu_u_uuuuuu_u_u__uuu___u_uu__u_u____________________________uuu___u_uu_____________uuu_uu_uu_uuuu______________u_uuuu_uu______uu_________uuuuu_u____uuu_u__uuu_u_uuu_uu__u_uuuu_uuuu_uuu____uuuu_u___________uuuu_________uu__uu_uu__________u____uu_uu_uuuu_uu___uu_uu_u_uu_uu__u_uu__uuu_u_u__uu__uu___uu_uuu_uu_uu_____uuuuuu__u_uuuu_uu___u___u____uuuu_u_uuu_uuu_u____u__u___uu_______u__uuu__uu__uu___u_uu_u_u_u___uu_u_u_______uu_uu___uuuuuu__uu__________________________________________________uuu__u_uu____________________uuu__uuuuu_u_uuuu_u__uuu_u_uuuu_uu___u__u__uu_____uu_uu________u_uuu___uu___uu________uu____u__uu_uu__uuuuuuu__u__uu_uu____uuu__uu_uu_u_uu_______uu___uuu__uu_uuuuuuuuu_u___u__uu____uu__u___uuuu_u_u______uu_u_uu_uu_u__uuuu___u__uuuu_uuu_uu__uuuuuu_uu_uu_uu____uuuu____uu__u_u______uuuu__uuu___uuuu____u__u_u_uuu_u____uuu_uuu_uu_uu_uuu_uu_______uuuuuuuuuuu________u__uu_u____uuuu______uu_u___u_uuuu_u__u_____uuuu_uuuuuuuu____uuuu___uuuuuu_uu__u__uu__________________________u_uuuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2SOBus.cpp +RES: ________uuuuu_u_uuuuu_uuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\UnipolarMotor.h +RES: ______________u_____uuuuuu_uuuuuuu__u______uuuu_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\UnipolarMotor.cpp +RES: _______uuuuuuuu_uu_u_uuuuuu_uu_u_uu__uu__u_uuuu_______________uu_uu_uuu_uu_uuu_uu_uuu_uu_uu__uu_uuu_uuu_uuu_uu____uuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Channel.cpp +RES: _______uuuuuu_uuu_uu_______uuuu________uuuu_uu_uuuu_u_uu_u_uu______uuuuu_uu____uuuuu_u__u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.cpp +RES: _______cc______ccccc_ccc_ccc__c_c_c_cu___cu___cu___cc_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Tokenizer.cpp +RES: ___________ccuuc_c_cuuuuu_c_u_c_cc___c___c____c_ccccc_ccccc_c_uu__uuuuu__uuu_u__cc__cu__cccccc__cuu_cu_c__ccc__c_____cccccc_cccccccu_c____cccccccu______c___c_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.h +RES: ________________________c______________________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\ParseException.h +RES: ___________________uuuuuuu_uuuu_uu_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\UserOutputs.cpp +RES: ___________uuuuuu_uuuuuuu_u_u_u_uu_uuuuu_uuuuu_uu_uuuuuu_uuuu_uuu_uu__uu__u_uuuu__uu__u_u_uu_uuuuuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepper.cpp +RES: ___________________________________________________________________uuu_uuu_uu____________________________________________________________________________________________________________________u_uuuu________uu_u__u_u_u_________uu__uuu____u_____uu__________u_uuu_uu_uu_uu__uu____uu_uu___u_u_uuuuuuuu__u_uu_uu__uu__u__u_u__uu_uuuu__u_u_u__uuuuuuuuu_u__uuuuu_u__u_uuuuu__uuuu__u_uuuuuuuuuuu__uu__uuuu______________u_uu__u_u_uuuu__uu___uuuuu_u_u___uu_u____uu____u________u__uuuuu_uuuuu___u_uu_uu_________uuu__u_uu_uuuu_u_uu__uuuuu__uuuuuuu___uu___u_uuuuuu_uuuu_uu_u_uuuu_uu___uu_u___u___u______u_______________uuu__uu_uu___u_uuuu_uuuuuu_u__uuu_uuuuuu_uuu_u____uu_uuuuu_u__uu_uuuuu___uuu__uuuuu__uuuu__u____uuu_uu______uuuu_u_uu___________uuuu__uu__uuu_u____________u_u____u___uuu_uuuu__u__uuu__uuu_u_u___uuu_uu_uuu_uu__uu_____uu_____u_u_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Validator.cpp +RES: _____________u_uu_uu_uu__u__uu_u_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Validator.h +RES: _____________________uu____uuuuuuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Error.cpp +RES: _______c____________________________________________________________________c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.h +RES: ________________________________________________________________________________________c____c___cc___c___c__ccccccc__________________c________________________________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.cpp +RES: _____________________c___cuuuc_cc__uuuuu_uuu__u_u__uuu_u__uuuuuuuuu_uu_uuuuu_uuuu_uuuuu__uuuuuu_uuu_u__uu_u_uuuuu____uu_uuu_uuuuuuu_u__u_uuu_uuuuuu_u__uuuuuuuu_u_uuuuuu__uuuuu_uuuu_uuuu_uu_____uuuuuuuuu_c_uuuuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.cpp +RES: _____________________________________uuuuu_uuuuu_uuu_u_uuuu_____u_uu__uu__uuu_u_uuuu_u_uu__uuu_u__uuu_uuu_u_uuuu__uuu__uuu_uu_uu_uu__uu_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.cpp +RES: ___________u_u__u_uu_uuu__uu__uuu__uuuuuuuuu____u_u_uuuuuuuu__u_uuuuuuu__u_uuuuuu_u__u_uuuuuuu__u_uuuuuuuu_u_uuuuuuuuu_u_uuuu_u_uuuuu___u_uuuuuuuuuu_uu____uuu_uuuuuuuu_uuuuuuuuu_u_uuuuuuuuu_uu_u_uuuuuuu____u_uuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.h +RES: __________________uuuuuuu_u___u________u_____u_u____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\NutsBolts.cpp +RES: _______________________uu__u_uuuuuu___uuuuuuuuuuu_uuuu_uuuuu_uu_uu____u__uuuuuuuu_uu___uuuu_uuu_uuu_uuu__uuuuuu_uuu__uu_uuuu__uuu_uuuuuu_uuuuuuuu___uuuuuuu_u____uu_uuuuuuu_uuu_uuu_u__uuuuu__uuuu_uuu_uuu_uuuu_uuu_uuu_uuu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\InputFile.h +RES: _____________________________________________________u___u______ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\HuanyangSpindle.cpp +RES: _______________________________________________________________________________________________________________________________________________u__u_u_uu__uu_u_uu_uu_u__u_u____uu______________________uu__uuuuu__u_uu__uu_uu_u_u_uu__uuuuu_u_uu__uu_u____uuu_u_uu__uu_u_______u_uuu_uu_uuu_u__u_u_u_uuuu_uu_u_uu_uuuuu_u_uu_uuuu______uu_u_______uu_u_uuu_uuu_u_uu__uuuuu_uuuu_uu_u_uu__uuuuu_uu__uuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\HuanyangSpindle.h +RES: ______________uuuu__________u__u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.cpp +RES: _________ccuuu_uu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.cpp +RES: _________________uuu__u_uuuu__u_u_uu_u_uu_uuu_uuuu_u_u___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.h +RES: _____________________________________u_u________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\CoreXY.cpp +RES: _____________________________u_u__uuuuu_uu__u_uuuuuuuuuu___uu__u__uuuuuu_u___u_u__uuuuuuuuuu_u_uuuu______u_uu__u_____uu_uu_u_uuu_u_u__u_uu_uuu_u_uuu__uuu__uuu_u_uu____uuu_uuuu_u__uu__uu_uu__uuuu_uuuuuu_uuuuuuu__u_u_u__uu_uuu_u__u__uuu_u_uu_uuu__u_uu_uu_u_u___________u_____uuuu_u__u_u_uu__u__uu_______u__uu_uuuu____uuu_uuuuu___uu_u___p__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\CoreXY.h +RES: ____________________________________u_u__u_u___________u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Stepping.cpp +RES: __________ccccc_uu_____uu__uu_____uu_uuu_uuuuu_uuuu__uu_u_u_uuu_u__uu__u_uu_uuu_u__u__u_u_uuuuuu_u__uuu_u___uu___uuu_u_uuuuuu_u_uuuuu_u_____________u___u_u______u_uu_uuuuuuuu_uuuuuu__u_uu__u_u__u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\xtensa\core-macros.h +RES: ____uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\OnOffSpindle.cpp +RES: _______uuuu__uuu_u_u___u_uuu__uuu_uuu____uuuu_u__u_uuuu_u_u_uuu__uu_u_uuuuuu___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Kinematics.cpp +RES: __________uuuu_uuuu_uuuu_uuuu_uuuu_uuu_u_uuuu_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\InputFile.cpp +RES: _________u______uuu_uuu_uu_uu_uuuuu__uuu_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Servo.cpp +RES: __________________________uuuu_uuu__uu________u_u_uu_uuuu_uuu_u_____uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Servo.h +RES: ________________u__________u_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ProcessSettings.cpp +RES: _________________________________uuu_u_u_uu_u_u_u___uu_uu_uu_uu_uu_uu_u__uuu____uu_u_uuuuu__uuu_uuuu_u_uuu_uu_uuu_uuuu_uu_uuu__uuuuuuuuu__uu_uuuuuu_uu__uuuuu________uuuuu_uuuu_uuuu_uuu__u_uuuuuuuuuuu_uuuuuuuu_uuuuuuuuu__uuuuuuuuuuuu_uuu_uuuuuuu_____uuuuuuu_uu_uuu_uuu_uuuu_uuuuu_uuuu_uu__uuuuuuuu_uuuuuuu_u__uu_uu__uu__u_u_u_u_uuuuu__uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_uuuuuuuu_uuuuuuuuuuuuuuu_uuuuuuuu_c____cuuu_uuu_uuu_u_uuu_uuu_uuu______uu__uuuu_uuuu_uuuuu_uuuuuu_uuuuuuu___uuuuu_uuuu_uuuuuuu_uuuuuuu___uuuuu_uuu__uuuuuuu__u_uuu__uu_uuu_uuu_uuu__uu_uuuuuuuuuu_uuu_uuu__uu_uuuuuuuuuu_uu_u_uu_uuu_uuuuuuu_uu_uuu_uu_uuuu_uuuu______uuuuuuuu_uuuuuuuuuuuuuuuu_uuuuuu_uuuu_u_uu______u_____uuu__uu_____u___u_uu___u________uuu_uu_uuuuuuuu_uuuuu_uuu_uuu___uuuu_uuuuu__u___uuuu_uuuuu__u___uuuu_u_u_____uuuuuuuu_uuuu_uuu_uuu_uu__uuu_u__u_uu_u_uu_u___u______uu_u_uuuu___uuuu_uu_uu_uu__uu__uu_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.h +RES: _____________________uu____uuuuuuuuu__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axis.cpp +RES: _______uuuuuuu__uu_uuuuuu_uuuuu_uuuuuu_uuuu____uuu_u_uuuuuuu__uuuuu_uuu__u__uuuuuu_uuu___u_uuuu_uu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\FileStream.h +RES: ___________________u_______uuuuu_______u_________u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Motor.cpp +RES: ____________uuuuuuuuuuu_uuu_u_uuu_u_uuuu_uuu_u__u__uuuuu_u_u_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.h +RES: _________u_u__u_u__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\JsonGenerator.cpp +RES: ____________uu_uuuu_uuuuuuuuuuu_uuu_u_uuuuu_uuuuu_uuuuu_uuuu_uuuu_uu_uuuuu_uu_uuuu_uuuu_uu_u_uuuuuu_u_________u_uuuuuu_uuu_uu_uu_u_uuuuuuuuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\JsonGenerator.h +RES: _______________________________uu_______________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Settings.cpp +RES: _____________uuuuuuuuuuuu__u_____uuuuu_____uuuu___uuuu_uuuuuuu_u_uuu_uu_uu___uuuu__u___________uuuu_uuuuuuu_u_uuuuuuu__u_uuuuu__uuu_uu___uu__uuuuuu_u__uuu_u_uuu_u___uuuuu_uu__uuu_uuuu_u__________uuuuuu_uuuuuuu___uuuuuuuu_uuu_uuuu_u_uuuu_uuu_uuuuuuuu_u__uuu_uuu_uu_u_uuuuu_uuu_uuu___________uu_uuuuuuu_u_uuuu_u_____uuuuu_uu___uuu__u_uuu_uuu_uuu__uuuuuuu_u__uuu_uuuuuuuuuuuuuuu_uuuuuuu_uuu_uuuuuuuuuu_uuu_uu__u_u_u_______u___u_u_uuuu_uu________u_u________uuuuuuu_u_uuuuuuu_u_uuuu_u_uuuuu_uuu_uuuuuuu_u__uuu_uuuuuuuuuu_uuuu_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homing.h +RES: _________________________________uuuuuuuuu__u_uuuuuuuuuuu_u___ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Motor.h +RES: ___________________________u_uu____u____________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\ControlPin.cpp +RES: _______uuuuu_u_uuu_uuuu_uuuu_uuu_u_uuu_uuu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\FileStream.cpp +RES: _________uuuu_uuuuuuu_uuu_uuu_uuu_u_uu_u_uu_u_uuuu_uu_u__uuu_uuuuuuu__u__uu___u__uuuu_u_uu_uuuu_u_u_uuuu_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Axis.h +RES: ____________________uuuuu____u_uuuuu_________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\EnumItem.h +RES: _____________c__c____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\SettingsDefinitions.cpp +RES: ___________c________c_c_uuuuuu_u_uu___uuuuuuuu_u_u__u_uu +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\PWMSpindle.h +RES: __________________________________u_u____________u_uu__u_u________u________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\OnOffSpindle.h +RES: ____________________uuuuu_uu____________________u_uuuu_u__u______u_u_____ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\System.cpp +RES: ____________________u_uuuuuuuuuu_uuuuuu_u_uuuuuuu_u_uuu_c__________c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.h +RES: ___________________________________________________cu___________________________________u_____________________ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.cpp +RES: __uuuuuuuu_c +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.cpp +RES: _________ccccc_c_uuuuuu_ccc_c_cccu_c_cc_uu_u_u_u_u_u_u_u_u_u_u_uc___cccccc_c___ccc_cccc__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.cpp +RES: _______________c_c_cc_____cu_uuuccc__ccc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c___c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_uu_uuu__uuuuuu_u_u__uuu_u_c_c__cc___ccc__cc___c_u +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.h +RES: ______________________c___________________cc___c___c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.h +RES: ______________________________c__ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\Extenders.cpp +RES: ______p_uuu_uuuuu_u_u_uuuuu_u_uu_uu_uuuuuuu_uuuu_uu_uuuuu_ +PROF: +FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Configurable.h +RES: _____________________u_u__c__ +PROF: diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index b7dea07c7..4dbc6ee83 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -17,21 +17,34 @@ namespace Extenders { I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} - uint8_t I2CExtender::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + uint8_t I2CExtender::I2CGetValue(uint8_t address, uint8_t reg) { + auto bus = _i2cBus; + + // First make sure the bus is empty, so we read that which we have written: + // while (bus->read(address, ®, 1) != 0) {} + int err; if ((err = bus->write(address, ®, 1)) != 0) { log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + + IOError(); return 0; } else { uint8_t result = 0; if (bus->read(address, &result, 1) != 1) { log_warn("Cannot read from I2C bus: " << "no response"); + + IOError(); + } else { + errorCount = 0; } return result; } } - void I2CExtender::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + void I2CExtender::I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { + auto bus = _i2cBus; + uint8_t data[2]; data[0] = reg; data[1] = uint8_t(value); @@ -40,16 +53,21 @@ namespace Extenders { if (err) { log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); + IOError(); + } else { + errorCount = 0; } } - void I2CExtender::isrTaskLoopDetail() { - std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); - int registersPerDevice = _ports / 8; - int claimedValues = 0; - uint8_t commonStatus = _operation; + void I2CExtender::IOError() { + if (errorCount != 0) { + delay(errorCount * 10); + if (errorCount < 50) { + ++errorCount; + } + } - // Update everything the first operation + // If an I/O error occurred, the best we can do is just reset the whole thing, and get it over with: _status = 1; if (_outputReg != 0xFF) { _status |= 4; // writes @@ -57,10 +75,22 @@ namespace Extenders { if (_inputReg != 0xFF) { _status |= 8; // reads } + } + + void I2CExtender::isrTaskLoopDetail() { + std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); + int registersPerDevice = _ports / 8; + int claimedValues = 0; + uint8_t commonStatus = _operation; + + // Update everything the first operation + IOError(); // Main loop for I2C handling: while (true) { - uint8_t newStatus = 0; + // If we set it to 0, we don't know if we can use the read data. 0x10 locks the status until we're done + // reading + uint8_t newStatus = 0x10; newStatus = _status.exchange(newStatus); newStatus |= commonStatus; @@ -89,9 +119,10 @@ namespace Extenders { if (_invertReg != 0xFF) { uint8_t currentRegister = _invertReg; uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { uint8_t by = _invert.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); currentRegister++; if (currentRegister == registersPerDevice + _invertReg) { @@ -101,12 +132,12 @@ namespace Extenders { } // Configuration: { - for (int i = 0; i < claimedValues; ++i) { - uint8_t currentRegister = _operationReg; - uint8_t address = _address; + uint8_t currentRegister = _operationReg; + uint8_t address = _address; + for (int i = 0; i < claimedValues; ++i) { uint8_t by = _configuration.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); currentRegister++; if (currentRegister == registersPerDevice + _operationReg) { @@ -138,7 +169,7 @@ namespace Extenders { for (int i = 0; i < claimedValues; ++i) { if ((toWrite & (1 << i)) != 0) { uint8_t by = handleInvertSoftware ? (_output.bytes[i] ^ _invert.bytes[i]) : _output.bytes[i]; - I2CSetValue(this->_i2cBus, address, currentRegister, by); + I2CSetValue(address, currentRegister, by); } currentRegister++; @@ -158,7 +189,7 @@ namespace Extenders { for (int i = 0; i < claimedValues; ++i) { auto oldByte = _input.bytes[i]; - auto newByte = I2CGetValue(this->_i2cBus, address, currentRegister); + auto newByte = I2CGetValue(address, currentRegister); if (handleInvertSoftware) { newByte ^= _invert.bytes[i]; } @@ -166,7 +197,7 @@ namespace Extenders { if (oldByte != newByte) { // Handle ISR's: _input.bytes[i] = newByte; - int offset = claimedValues * 8; + int offset = i * 8; for (int j = 0; j < 8; ++j) { auto isr = _isrData[offset + j]; if (isr.defined()) { @@ -181,13 +212,15 @@ namespace Extenders { } currentRegister++; - if (currentRegister == registersPerDevice + _invertReg) { + if (currentRegister == registersPerDevice + _inputReg) { ++address; } } } } + _status &= ~0x10; + vTaskDelay(TaskDelayBetweenIterations); } } @@ -334,7 +367,7 @@ namespace Extenders { _status |= 8; } while (_status != 0) { - vTaskDelay(1); + vTaskDelay(1); // Must be +#include #include #include #include @@ -8,6 +9,26 @@ #include "Capture.h" +#include + +namespace { + struct GPIONative { + // We wire 15 to 20. + static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { + // switch (pin) { + // case 20: + // pins[15].handlePadChange(value); + // break; + // } + } + + inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, true); } + inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } + inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } + inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } + }; +} + namespace Configuration { Test(I2CExtender, I2CBasics) { // Initialize I2C bus @@ -147,13 +168,16 @@ namespace Configuration { Roundtrip() { before = Capture::instance().current(); } ~Roundtrip() { - while (Capture::instance().current() < before + 1) { - delay(10); + for (int i = 0; i < 10; ++i) { + while (Capture::instance().current() < before + 1) { + delay(10); + } + before = Capture::instance().current(); } } }; - Test(I2CExtender, SetupPin) { + Test(I2CExtender, ExtenderNoInterrupt) { // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -191,8 +215,6 @@ namespace Configuration { // high = 1, low = 0 { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -202,8 +224,8 @@ namespace Configuration { i2c.setupPin(0, Pin::Attr::Output); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -219,7 +241,7 @@ namespace Configuration { { Roundtrip rt; } auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); + Assert(recv.size() == 1, "Expected single data request / response roundtrip, got %d", int(recv.size())); Assert(recv[0] == 0, "Expected read"); Assert(readPin == true, "Expected 'true' on pin"); } @@ -269,8 +291,6 @@ namespace Configuration { // Setup pin for reading: { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -280,8 +300,8 @@ namespace Configuration { i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -292,8 +312,6 @@ namespace Configuration { // Setup another pin for reading with an invert mask and a PU: { - Roundtrip rt; - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. // Let's just set it 'high': @@ -303,8 +321,8 @@ namespace Configuration { i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - } - { + { Roundtrip rt; } + auto buffer = Wire.Receive(); Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); @@ -337,4 +355,428 @@ namespace Configuration { Assert(readPin == false, "Expected 'true' on pin"); } } + + Test(I2CExtender, ExtenderWithInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender with ISR on gpio.15 + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(true); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + // Expected register values (see datasheet): + // + // 4 invert + // 1 = invert, 0 = normal + // + // 6 config + // 1 = input, 0 = output + // + // 2 write + // 1 = high, 0 = low + // + // 0 read + // high = 1, low = 0 + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x01); + + i2c.claim(0); + i2c.setupPin(0, Pin::Attr::Output); + { Roundtrip rt; } + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), + uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + } + + // Read will NOT trigger an update because we have an ISR to tell us when it changes: + { + bool readPin = i2c.readPin(0); + auto recv = Wire.Receive(); + + Assert(recv.size() == 0, "Expected single data request / response roundtrip, got %d", int(recv.size())); + Assert(readPin == true, "Expected 'true' on pin"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Test write pin: + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'low'. + i2c.writePin(0, false); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 0, "Expected write reg 0 = 0"); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'low'. It's already low, no-op. + i2c.writePin(0, false); + i2c.flushWrites(); + // no-op. + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + { + // Write to set it 'high'. + i2c.writePin(0, true); + i2c.flushWrites(); + { Roundtrip rt; } + auto recv = Wire.Receive(); + + Assert(recv.size() == 2, "Expected single data request / response roundtrip"); + Assert(recv[0] == 2, "Expected write reg 0"); + Assert(recv[1] == 1, "Expected write reg 0 = 0"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // NOTE: We ended with setting pin #0 to 'high' = 0x01 + + // Setup pin for reading: + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + + i2c.claim(1); + i2c.setupPin(1, Pin::Attr::Input); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), + uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Setup another pin for reading with an invert mask and a PU: + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x04); + + i2c.claim(2); + i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), + uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; + Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(1); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Trigger an ISR, change both pins + { + Wire.Send(0x02); + GPIONative::write(15, true); + GPIONative::write(15, false); + { Roundtrip rt; } + auto recv = Wire.Receive(); + Assert(recv.size() == 1); + Assert(recv[0] == 0); + } + + // Test read pin: + { + bool readPin = i2c.readPin(1); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + } + + void HandleInterrupt(void* data) { ++(*reinterpret_cast(data)); } + + volatile uint16_t currentInput = 0; + void WireResponseHandler(TwoWire* theWire, std::vector& data) { + if (data.size() == 1) { + if (data[0] == 0) { + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(currentInput)); + data.clear(); + } else if (data[0] == 1) { + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(currentInput >> 8)); + data.clear(); + } else if (data[0] >= 2 && data[0] <= 7) { + // ignore until next roundtrip + } else { + Assert(false, "Unknown register"); + } + } else if (data.size() == 2) { + if (data[0] >= 2 && data[0] <= 7) { + data.clear(); + } else { + Assert(false, "Unknown register"); + } + } else { + Assert(false, "Unknown size"); + } + } + + Test(I2CExtender, ISRTriggerWithInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(true); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + uint32_t isrCounter = 0; + + { + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + { Roundtrip rt; } + + // Test read pin: + { + bool readPin = i2c.readPin(9); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Change state, wait till roundtrip + { + Wire.Send(0x00); + Wire.Send(0x02); + + // Trigger ISR pin 'falling' + GPIONative::write(15, true); + GPIONative::write(15, false); + { Roundtrip rt; } + + auto recv = Wire.Receive(); + Assert(recv.size() == 2); + Assert(recv[0] == 0); + Assert(recv[1] == 1); + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin = i2c.readPin(9); + Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == true, "Expected 'true' on pin"); + } + } + + Test(I2CExtender, ISRTriggerWithoutInterrupt) { + GPIONative::initialize(); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. + // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. + // Let's just set it 'high': + Wire.Send(0x00); + Wire.Send(0x00); + + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto buffer = Wire.Receive(); + Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); + + const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), + uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), + uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; + Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + } + + uint32_t isrCounter = 0; + + { + // From this point on, we just need to respond to wire requests + currentInput = 0x0000; + Wire.Clear(); + Wire.SetResponseHandler(WireResponseHandler); + + i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); + } + + // Test read pin: + { + bool readPin = i2c.readPin(9); + Assert(readPin == false, "Expected 'true' on pin"); + } + + // Change state, wait till roundtrip + { + currentInput = 0x0200; + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin = i2c.readPin(9); + Assert(readPin == true, "Expected 'true' on pin"); + + { Roundtrip rt; } + + // Test if ISR update went correctly: + Assert(isrCounter == 1); + + // Test read pin: + bool readPin2 = i2c.readPin(9); + Assert(readPin2 == true, "Expected 'true' on pin"); + } + + Wire.Clear(); + } } diff --git a/FluidNC/test/Pins/GPIO.cpp b/FluidNC/test/Pins/GPIO.cpp index 39ec3d49d..e9dd09635 100644 --- a/FluidNC/test/Pins/GPIO.cpp +++ b/FluidNC/test/Pins/GPIO.cpp @@ -8,38 +8,41 @@ extern "C" void __pinMode(uint8_t pin, uint8_t mode); extern "C" int __digitalRead(uint8_t pin); extern "C" void __digitalWrite(uint8_t pin, uint8_t val); -struct GPIONative { - inline static void initialize() { - for (int i = 16; i <= 17; ++i) { - __pinMode(i, OUTPUT); - __digitalWrite(i, LOW); +namespace { + struct GPIONative { + inline static void initialize() { + for (int i = 16; i <= 17; ++i) { + __pinMode(i, OUTPUT); + __digitalWrite(i, LOW); + } } - } - inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); } - inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); } - inline static bool read(int pin) { return __digitalRead(pin) != LOW; } -}; + inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); } + inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); } + inline static bool read(int pin) { return __digitalRead(pin) != LOW; } + }; +} #else # include -struct GPIONative { - // We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17: - static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { - switch (pin) { - case 16: - case 17: - pins[16].handlePadChange(value); - pins[17].handlePadChange(value); - break; +namespace { + struct GPIONative { + // We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17: + static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) { + switch (pin) { + case 16: + case 17: + pins[16].handlePadChange(value); + pins[17].handlePadChange(value); + break; + } } - } - - inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); } - inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } - inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } - inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } -}; + inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); } + inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); } + inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } + inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } + }; +} void digitalWrite(uint8_t pin, uint8_t val); void pinMode(uint8_t pin, uint8_t mode); int digitalRead(uint8_t pin); diff --git a/X86TestSupport/TestSupport/SoftwareGPIO.h b/X86TestSupport/TestSupport/SoftwareGPIO.h index 061578f9b..7332c1c98 100644 --- a/X86TestSupport/TestSupport/SoftwareGPIO.h +++ b/X86TestSupport/TestSupport/SoftwareGPIO.h @@ -128,7 +128,7 @@ class SoftwareGPIO { pins[index].handlePadChangeWithHystesis(value); } } else { - pins[index].driverValue = value; + pins[index].handlePadChange(value); } } diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 69373b035..2ada77bbe 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -1,10 +1,11 @@ #include "Wire.h" +#include + TwoWire Wire(0); TwoWire Wire1(1); -TwoWire::TwoWire(uint8_t bus_num) {} -TwoWire::~TwoWire() {} +TwoWire::TwoWire(uint8_t bus_num) : handler(nullptr) {} // For unit tests: void TwoWire::Send(std::vector data) { @@ -13,17 +14,21 @@ void TwoWire::Send(std::vector data) { } } void TwoWire::Send(uint8_t value) { + std::lock_guard guard(mut); receivedData.push_back(value); } std::vector TwoWire::Receive() { - std::vector data; + std::lock_guard guard(mut); + std::vector data; std::swap(sentData, data); return data; } void TwoWire::Clear() { + std::lock_guard guard(mut); sentData.clear(); receivedData.clear(); + handler = nullptr; } // TwoWire interface: @@ -81,6 +86,8 @@ uint8_t TwoWire::endTransmission(void) { } size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { + std::lock_guard guard(mut); + auto available = receivedData.size(); if (available > size) { available = size; @@ -113,8 +120,15 @@ uint8_t TwoWire::requestFrom(int address, int size) { } size_t TwoWire::write(uint8_t ch) { - Assert(inTransmission, "Should be in a transmission"); - sentData.push_back(ch); + { + Assert(inTransmission, "Should be in a transmission"); + std::lock_guard guard(mut); + sentData.push_back(ch); + } + + if (handler) { + (*handler)(this, sentData); + } return 0; } @@ -125,9 +139,11 @@ size_t TwoWire::write(const uint8_t* buf, size_t size) { return size; } int TwoWire::available(void) { + std::lock_guard guard(mut); return receivedData.size(); } int TwoWire::read(void) { + std::lock_guard guard(mut); if (receivedData.size()) { auto result = receivedData[0]; receivedData.erase(receivedData.begin()); @@ -137,6 +153,7 @@ int TwoWire::read(void) { } } int TwoWire::peek(void) { + std::lock_guard guard(mut); if (receivedData.size()) { auto result = receivedData[0]; return result; @@ -146,5 +163,7 @@ int TwoWire::peek(void) { } void TwoWire::flush(void) {} +TwoWire::~TwoWire() {} + extern TwoWire Wire; extern TwoWire Wire1; diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index 1bda1ea24..b28f807ca 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -2,6 +2,8 @@ #include #include +#include + #include "esp32-hal-i2c.h" #include "Stream.h" #include "../FluidNC/test/TestFramework.h" @@ -10,6 +12,10 @@ class TwoWire : public Stream { bool inTransmission = false; std::vector receivedData; std::vector sentData; + std::mutex mut; + + using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data); + ResponseHandler handler; public: TwoWire(uint8_t bus_num); @@ -18,8 +24,11 @@ class TwoWire : public Stream { // For unit tests: void Send(std::vector data); void Send(uint8_t value); + size_t SendSize() { return receivedData.size(); } std::vector Receive(); + size_t ReceiveSize() { return sentData.size(); } void Clear(); + void SetResponseHandler(ResponseHandler handler) { this->handler = handler; } // TwoWire interface: From dff43d2510ab9342c6c44470f07bb121f336f2f4 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 3 Mar 2022 17:35:00 +0100 Subject: [PATCH 084/100] Small fixes --- FluidNC/src/Extenders/I2CExtender.cpp | 1 + FluidNC/src/Main.cpp | 88 --------------------------- 2 files changed, 1 insertion(+), 88 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 4dbc6ee83..9a378b36b 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -219,6 +219,7 @@ namespace Extenders { } } + // Remove the busy flag, keep the rest. _status &= ~0x10; vTaskDelay(TaskDelayBetweenIterations); diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 42941e620..4626a7d84 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -28,51 +28,6 @@ extern void make_user_commands(); -// FOR TESTING: - -# include -# include - -extern "C" void __pinMode(pinnum_t pin, uint8_t mode); - -uint8_t I2CGetValue(uint8_t address, uint8_t reg) { - Wire.beginTransmission(address); - Wire.write(reg); - auto err = Wire.endTransmission(); // i2c_err_t - - if (Wire.requestFrom((int)address, 1) != 1) { - Uart0.println("Error reading from i2c bus."); - return 0; - } - uint8_t result = Wire.read(); - return result; -} - -void I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { - uint8_t data[2]; - data[0] = reg; - data[1] = uint8_t(value); - - Wire.beginTransmission(address); - for (size_t i = 0; i < 2; ++i) { - Wire.write(data[i]); - } - auto err = Wire.endTransmission(); // i2c_err_t ?? - - if (err) { - Uart0.println("Error writing to i2c bus; PCA9539 failed. Code: "); - Uart0.println(int(err)); - } -} - -volatile bool fired = false; - -void isrHandler() { - fired = true; -} - -// --- Until here. - void setup() { try { uartInit(); // Setup serial port @@ -84,49 +39,6 @@ void setup() { WebUI::WiFiConfig::reset(); - /* TEST STUFF! */ - - /* - // THIS WORKS: - { - Uart0.println("Basic test of pin extender."); - // Wire.begin(sda , scl, frequency); - Wire.begin(13, 14, 100000); - - Uart0.println("Setup pins:"); - - // 1. Setup pins: - I2CSetValue(0x74, 6, 0xFF); // All input pins - I2CSetValue(0x74, 7, 0xFF); // All input pins - - __pinMode(36, INPUT); - attachInterrupt(36, isrHandler, CHANGE); - - // 2. Read input register: - Uart0.println("Main loop:"); - while (true) { - auto r1 = I2CGetValue(0x74, 0); - auto r2 = I2CGetValue(0x74, 1); - uint16_t v = (uint16_t(r1) << 8) | uint16_t(r2); - Uart0.print("Status: "); - for (int i = 0; i < 16; ++i) { - uint16_t mask = uint16_t(1 << i); - Uart0.print((v & mask) ? '1' : '0'); - } - - if (fired) { - Uart0.print(" ** ISR"); - fired = false; - } - - Uart0.println(); - - delay(1000); - } - } - */ - /* END TEST STUFF */ - display_init(); // Load settings from non-volatile storage From bcce6988c372a64b836370abef91541fd8796dda Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Mar 2022 14:03:35 +0100 Subject: [PATCH 085/100] Fixed unit tests and a few more small bugs. --- CodeCoverage.cov | 54 +- FluidNC/src/Extenders/I2CExtender.cpp | 7 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 554 +++++++++++---------- X86TestSupport/TestSupport/Wire.cpp | 5 +- X86TestSupport/TestSupport/Wire.h | 8 +- 5 files changed, 331 insertions(+), 297 deletions(-) diff --git a/CodeCoverage.cov b/CodeCoverage.cov index a3f4cc636..8c6613e6f 100644 --- a/CodeCoverage.cov +++ b/CodeCoverage.cov @@ -14,13 +14,13 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Stand RES: _______________________u_uuuu_uu_uuuu_u_uu_uu_u_uuuuuuuuuuu_uuuu_uuu__uuuuuuuuuuu__uu_uuu_uuuuuu_u_uuu_u_u_u___c__uuuuuuu__uuuu___u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\WString.h -RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________ccccuuuu__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ +RES: _____________________c______ccuucuuuuu_ccccuuuu_____u_____uuuuuuuuuuuu_uuuu_cccc_uuuu_uuuu_uuuu_uuuu_uuuu____uuuuuuuu___________________________________________ucucuc______cccc___________________u______cccuu__u___________cccccccc__uuuuu_uuuuuuuuu_____uu__uuuuu_________cuu_______________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Authentication.cpp RES: ___________________________________________u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.cpp -RES: ____cc_c__ccccccccccccccc_cccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_u___ +RES: ____cc_c__ccccccccccccccc_ccccccc____uuu_ccc_uuuuuu_uuuu_uuuuuu_uuuuccccuuuu_uuuuuccccc_cc_ccc_ccuuuuuuuuuuuuuuuuuuuuuccc_c_cccc_cc_cc_uuuuuuuuuuccccccuu_uuuuuuuu_uu_c___ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\PinExtenderDriver.cpp RES: _______uuuu_ @@ -35,25 +35,25 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSuppo RES: ____cuuuuuuuuu_c__c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Wire.h -RES: ___________c______________c_c_c_________________________________________________ +RES: ___________c_______________c___cccc_________________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.cpp RES: ______________________________u_u_uuu_uuu__u_u_uuu_uuu___u_uuuu_uu_uu_uuu_____uuuuuu__uuu___uuuuuuuu_uuu_________________________________________________________________________________________uuu___uuu__u_uu___u_uuuu_uuu_uu_uu__uuu___uuuu_u_u_uu___u_uuuuuuuu__uuu_uu_uuuu_u______uuuuuu_uuuuu_____uuu_uuuuu_uuuuu_uuuuuuuuu_uuuuuuuuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SPIFFS.cpp -RES: ____cu___uuuuuuu__c +RES: ____cc___uuuuuuu__c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\NullMotor.cpp RES: ____________c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ErrorPinDetail.cpp -RES: ________c_u____________cccuucc___u_u_ +RES: ________c_c____________cccuucc___u_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Print.cpp RES: ______________________________________ccccccc_u_u__uuuuuuu_uuuuu_u_uuuu_uu_uuu_ccc_uuu_uuu_ccc_ccc_cccuu_cc_ccuuc_c_uuuuu_uu_uuuuu_u_uuu_uuu_uuuu__uuu_uu_uuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu_uuuuu___c_c_c__cu___cc_cc_cc_u_u_u__uu___uuu_uu_uu_uu_uu_uu_uu_uu___uuu___uuuu_u__uuu__uu___uuuuuu_uu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.h -RES: ___________________________________________________________________________________c______c______c________u__cccc__uu_cc___cccc____c_c_u_uu_c____c_c__u_c___u___ +RES: ___________________________________________________________________________________c______c______c________u__cccc__cc_cc___cccc____c_c_u_cc_c____c_c__c_c___u___ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\driver\rmt.cpp RES: ____uuu_u_uuu_uu @@ -86,7 +86,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuratio RES: ________u_u_uuu_uu_uuuu_uu_uuu_uuu_u_uuuuuu__u_uu_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\freertos\Task.cpp -RES: _____________c_______ccccc_cccp_uuu_uuuuu_uuu_uuu_uuu_uuu +RES: _____________c_______ccccc_cccc_uuu_uuuuu_uuu_uuu_uuu_uuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Capture.h RES: _______________________c__cccc___uuuuuuuu__________________________ccuuu_u_c_____uuuu_____________uuuuuu_u_uu_uuuuu_u_ @@ -107,10 +107,10 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Limi RES: _____________u_____uu___u_________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.cpp -RES: __________c_ccu_uuuu_u_uuu___uu_u__uuu_uuuuu_u_uuu_u_c_c_u_u_uu_u_uu_uuuu_c_cuucuuuu_ccc_ +RES: __________c_ccc_cccc_c_ccc___cc_c__ccc_ccccc_c_ccc_c_c_c_c_c_cc_c_cc_cccc_c_cccccccc_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\GPIO.cpp -RES: _____________________________uu__uu__u_u__u_________pu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu_uu_uu_uu_uuuuuuu______________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu_p_p_p_pu_uu_uu_uu_uuuu_u_uuuu_u_uuuuu_pu_uu_uu_uu_uuuuu_u_uuuu_u_uuuuu_pu_uuu_pu_uuu_pu_uu_uu_uuuu_u_uuuu_u_uuuuu______uu_uu_uu_uu_____________uuuuuu_uuu__uuuuu_uuu_u__u_uuuuu_uu__p_p_p_ +RES: _____________________________cc__cc__c_c__c_________cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_ccccccc______________cccccc_ccc__ccccc_ccc_c__c_ccccc_cc_c_c_c_cc_cc_cc_cc_cccc_c_cccc_c_ccccc_cc_cc_cc_cc_ccccc_c_cccc_c_ccccc_cc_ccc_cc_ccc_cc_cc_cc_cuuu_u_uuuu_u_uuuuu______cc_cc_cc_cc_____________cccccc_cuc__ccccc_cuu_u__u_uuuuu_uu__c_c_c_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\LedcPin.cpp RES: ______________________u________uuuu_uu_u_u_u____uuuuuu_uuu____uuuu @@ -122,7 +122,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\10v RES: ___________________________________u_uuuuu__u_u_________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.3\build\native\include\gtest\internal\gtest-internal.h -RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________p____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ +RES: _____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________u______c__________c____________________cc____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\StepStick.cpp RES: __________u_uuu_uu__u_uu_uuuuu_uuu__uu_u__u___c__ @@ -149,7 +149,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Spi RES: ________________u_uuuu_uuuuu_u_uuuuu_u__uu_uuu_uuuu_________uuu_uuuu__uuu__uuuuu_uuuu_u_uuuuu_uuuuuuu_uu_uuu_uuuu_uu_u_uuu_uu_______uu___uuuuu___u_u_uu_u__u___u_u__uu_uuuu_u_uu___u_u__uu_uuu_uuuu____uu_uu_uuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\GPIOPinDetail.cpp -RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__cuuuuuu_uuuuuuuuu_uu_uc__cc_u_c_uuu_uuuuuuuu_c_____c__c___c__c_ccuu___cucu___cu__cc_cccc_cccc_cccu_cu_cu__cc_ +RES: ________________c_c_c_u____u__u___________c___________c_____u_________u______uu__u_c__c______ccc__ccuuuuu_cuuuuuccu_uu_cc__cc_u_c_ccu_cccccccc_c_____c__c___c__c_cccc___cucu___cc__cc_cccc_cccc_cccu_cu_cu__cc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\AfterParse.cpp RES: ____________uu_uu__u__uu_u_uu_ @@ -167,7 +167,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Motors\Motor RES: ____________________________uuu_u_u_uuuuuuuuuuuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinOptionsParser.h -RES: ______________________________________u____u__uu__uc____________cc__ +RES: ______________________________________u____c__cc__cc____________cc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\Completer.cpp RES: _____________u_uuu_uu_uuuuu____uu__u_uu_uuuu_u_u_____________uu_u_uu__uuuu_uuuuu_uuu_u_uu_uu @@ -347,13 +347,13 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\ExtPinD RES: _______________u___________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Main.cpp -RES: _____________________________________uuuu_uuu_uuu_u_uu_uuuuu_uuu_u___uuu___uuu___u_u____________________________________________u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ +RES: ______________________________uuu___u_u_u__u_uu_uu__u_u_uuu__uu_uu_uu__uu___uu__u_u_uuuu_u_u___uu_uuu__u________u_u_uuuu_uu___uuu_uu_uuu_u___________uuu_____u_uuuu_u___uuuuu_u_uu____u____________uuuu_uuu_u_u____________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.cpp -RES: _______cu_u_uu_uuuu_u__ +RES: _______cu_c_cc_ccuu_c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\VoidPinDetail.h -RES: ________________________u__ +RES: ________________________c__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Limits.cpp RES: __________________u________________u_____uuu_uuuuu_uu____uuu_uuuuuu_uu__uu____uu_uu_uuuuuuu_uu___uu_uuuu_u_uuuu_u_______________________uuuuu__uu_uuuuu__uu @@ -401,7 +401,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\Homi RES: ________________________________________uuuuu_uuu_u_uuu___u__u_uu_u_u___u_uuuu_uuuuuuu_______u_uuuu_u___uuuuu_u__uuuuuuuuu_uu_uuuu_uu_u__uu__u_uu__u__u_uu__u_____u_u_u_u_uuu_u__u_u__u_uu_uuu___uuu_u__uuuuu__uu_u__uu___uuu_u__uuuuu___uu_u_uu___________u________uu__uuu_uuuu_uuu_uu_uuuuu_uuuu_u_uuuuuu__u___uu_uuuuuu__uuuu_uuu_uu__u__uuu_u_u__u___________uuu_uuuu__u_uuuuuuuu_uuu_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.h -RES: __________________________________u____ +RES: __________________________________c____ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Commands.cpp RES: __________________________uuuu_uuu_uuuu___uu___uuuu_uuu____u____uu_uuu_u_ @@ -416,28 +416,28 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinUsers\Pwm RES: _________________u_____________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Arduino.cpp -RES: ________uuu_uu_uuuu__u_ccc_ccc_uuuu_cccc_uuuu_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ +RES: ________uuu_uu_uuuu__u_ccc_ccc_cccc_cccc_cccc_ccc_uuu_uuu_uuuuuuuuuuuuuuu_uuu__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Kinematics\Cartesian.h RES: ________________________________uuu__u__u__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\SoftwareGPIO.h -RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_uu_u_cc_c_uu___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cuuuu__c_cc_cuuuuuu_uc_c_u_ccc_cccc_ccccc_ +RES: ________c_________c_cccccccc_uuuuuu__uuuu_u_uuuuu_u_cccc_cc_c_cc_c_cc___c_c_____c___cc__cccc_ccccc_ccc_ccccc_cccuu__c_cc_cccuuuu_cc_c_c_ccc_cccc_ccccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\PinOptionParsing.cpp -RES: _______puu__uuu___uuu_uuuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uu___uuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuu_uuu_uu___uuuuuuuu_uuu_pu_u_u__uuuuuuu_uuuuuu_uu___uuuuuuuu_uuu_ +RES: _______ccc__ccc___cuu_cuuc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_cc___cccccu_ccc_cc_c_c__cccc_ccc_cc___ccccuuuu_ccc_cc_c_c__cccc_ccc_cc___ccccuuuu_ccc_cc_c_c__ccccccc_cccccc_cc___ccccuuuu_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\TestFactory.cpp RES: ______________________________________uu__uu____uu__uu_uuuu_u_uuu__u_u_uuu_u_uu_uu_uuuu_uu_uu_uu_uu__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\Undefined.cpp -RES: ______p__uu__uuu___uuu__uu_uuuu_p_uu_uu__uu_uu__uu_uu__uu_uuu_ +RES: ______c__cc__ccc___ccc__pc_cccc_c_cc_cc__cc_cc__cc_cc__cc_ccc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Pins\ErrorPinTest.cpp -RES: ______c__c_cc_c_cc_pu_uu_ +RES: ______c__c_cc_c_cc_pc_cc_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\test\Extender\I2CExtenderTests.cpp -RES: ________________u_____u_c_c_____c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__c_cccccc__ccc__cccccc_c_cccccc__ccc__ccccc_cccccccccccccc_____c_cccccccc__c_cccccc__ccc_c__ccccc___________________c_cc__c_cc_uuuu___uuuu_uuuu____uuuu_uuuu__uuuu_uuuu__uu____uuuu_uuuu________u_uu__u_uu_uuuu______u_uu__u_uu_uuuu___uuuu_uuuu___uuuu_uuuuu_cc__cccccc__ccc_c__ccccc___________________c_ccc__cc_cccc___cc_cc_cc____cccc_ccccc__cccc_ccccc__cc__c___cccc_ccc_cc________c_cc__c_cc_ccc_cc______c_cc__c_cc_ccc_cc___ccc____ccc____cccccccc___ccc____ccc_c_c__ccccccccccc_cucccccu_cu_c_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c__cc_c__c_cc_ccccc_c___ccc____cc__ccc_ccuuu__u__uuuuu_cc__cccccc__ccc_c__ccccc_____cc_cc__c_cc_ccccc_c___ccc_c____cc____cc__c__cc_c__c__cc__cc_ +RES: _________________u_____u_c_c__________cc___ccc__cc_cc__ccc_ccc__cc_cc_u__c_ccccccc__cccc_cuccccccu_cu_c__cccccccc_cc_c_ccc_ccc_c_cc_ccc__cc___c_ccc_c_cu_c_c_cccccc______c_cccccccc__c___c_ccccc_cc_c_cc_ccc__cc_cccc_ccccc______uu__c_uuuccc_cuccc_c_u_u_uccc_c__cc_c__cccccc__ccc_cc__cccccc_ccc__cccccc__ccc_cc__ccccc_cccccccccccccc_ccc__cccccc__ccc_cc__ccccc____cc__c__cc____cccc_____ccccc___ccccc___cc_cc___cccc_ccc_____cc__c_ccc____cc__c_cccc____cc_cc____cc_cc__cc___cc_cc____cc_cc_c_cccc__cccccc__ccc_cc__ccccc__ccc_c____ccc_____cccc___cccc___cc__c___cccc______cc__cc____cc__cc____cccc____cccc____cccc____ccc____ccc_c_c_cccc__cccccc__ccc_cc__ccccc__cc__c_c__c__c__c_c__c___ccc____cc__cc__ccc___c__c_c____cc__cc_c_cccc__cccccc__ccc_cc__ccccc__cc__c_c__c___c____cc____c_c__c__cc___c_c__c__cc___c__c_c____cc__c_c_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CBus.h RES: ______________c____c__c_____________ @@ -479,7 +479,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\WebUI\Notifi RES: ____________u__________________________________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pin.cpp -RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccu_u_u_u________c__cc______cu__c_u__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccu_uuu_ccc_c +RES: _______________cc_cc__c____cu_c_uu__cccc_c_cc__cccccc___cuu_ccc_u_c_c________c__cc______cu__c_c__cuuuu_u___cuuu____c_u_uuu_cccccuu__uuuc____u_u______uc_u__uuu__uu_uuu_u_ccc_ccc_ccc_c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\BESCSpindle.h RES: ________________________________u_______uu_______________u_uu_uuu_u__u_u__ @@ -533,7 +533,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Uart.cpp RES: _____________________c___cuuuc_cc__uuuuu_uuu__u_u__uuu_u__uuuuuuuuu_uu_uuuuu_uuuu_uuuuu__uuuuuu_uuu_u__uu_u_uuuuu____uu_uuu_uuuuuuu_u__u_uuu_uuuuuu_u__uuuuuuuu_u_uuuuuu__uuuuu_uuuu_uuuu_uu_____uuuuuuuuu_c_uuuuu_uuu PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\PinMapper.cpp -RES: _____________________________________uuuuu_uuuuu_uuu_u_uuuu_____u_uu__uu__uuu_u_uuuu_u_uu__uuu_u__uuu_uuu_u_uuuu__uuu__uuu_uu_uu_uu__uu_uuu_uuu +RES: _____________________________________ccccc_ccccc_cuc_c_cccc_____u_cc__cc__uuu_u_uuuu_u_uu__ccc_c__ccu_ccc_c_ccuu__ccu__ccc_cc_cu_cu__cc_ccu_ccc PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Configuration\RuntimeSetting.cpp RES: ___________u_u__u_uu_uuu__uu__uuu__uuuuuuuuu____u_u_uuuuuuuu__u_uuuuuuu__u_uuuuuu_u__u_uuuuuuu__u_uuuuuuuu_u_uuuuuuuuu_u_uuuu_u_uuuuu___u_uuuuuuuuuu_uu____uuu_uuuuuuuu_uuuuuuuuu_u_uuuuuuuuu_uu_u_uuuuuuu____u_uuu_ @@ -554,7 +554,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\Hua RES: ______________uuuu__________u__u_____ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinDetail.cpp -RES: _________ccuuu_uu__ +RES: _________ccucc_uc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Spindles\DacSpindle.cpp RES: _________________uuu__u_uuuu__u_u_uu_u_uu_uuu_uuuu_u_u___c__ @@ -647,7 +647,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\System.cpp RES: ____________________u_uuuuuuuuuu_uuuuuu_u_uuuuuuu_u_uuu_c__________c PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\X86TestSupport\TestSupport\Stream.h -RES: ___________________________________________________cu___________________________________u_____________________ +RES: ___________________________________________________cc___________________________________u_____________________ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\StartupLog.cpp RES: __uuuuuuuu_c @@ -656,7 +656,7 @@ FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Machine\I2CB RES: _________ccccc_c_uuuuuu_ccc_c_cccu_c_cc_uu_u_u_u_u_u_u_u_u_u_u_uc___cccccc_c___ccc_cccc__ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Extenders\I2CExtender.cpp -RES: _______________c_c_cc_____cu_uuuccc__ccc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c___c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_uu_uuu__uuuuuu_u_u__uuu_u_c_c__cc___ccc__cc___c_u +RES: _______________c_c_cc_____cu_uuuccu__uuc_c_ccc__cc_c_cuuuc_c_ccuuu____ccc_cc_c_ccccc__c__c__c_cc_cccc___c_c____cccc_c_ccc_ccc_ccc_c___cc_ccc_ccc_c___ccc_cc__c___ccc_c_ccccc__ccc_c___ccc__c_ccccu__c_cccccccccc__c__ccc_c____c_ccc_c_cc_cc_cc_cccc_ccc__cc_c___cccc_cccc_ccc_c__ccccccc__u____c_c________ccc_c_cc_c_c_cc_cccuccc_cu___cc_____ccc_cc_cccccc___ccc___c_cc______cc_ccc__cc_cc_cc_cccc_ccc_ccc__ccc___ccc_cc_ccc__cccccu_c_c__ccc_u_c_c__cc___ccc__cc___c_u_ PROF: FILE: C:\Users\atlas\Desktop\Opensource\atlaste\FluidNC\FluidNC\src\Pins\PinAttributes.h RES: ______________________c___________________cc___c___c__ diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index 9a378b36b..fa7b2bbf8 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -21,7 +21,7 @@ namespace Extenders { auto bus = _i2cBus; // First make sure the bus is empty, so we read that which we have written: - // while (bus->read(address, ®, 1) != 0) {} + while (bus->read(address, ®, 1) != 0) {} int err; if ((err = bus->write(address, ®, 1)) != 0) { @@ -42,6 +42,7 @@ namespace Extenders { return result; } } + void I2CExtender::I2CSetValue(uint8_t address, uint8_t reg, uint8_t value) { auto bus = _i2cBus; @@ -68,7 +69,7 @@ namespace Extenders { } // If an I/O error occurred, the best we can do is just reset the whole thing, and get it over with: - _status = 1; + _status |= 1; if (_outputReg != 0xFF) { _status |= 4; // writes } @@ -98,7 +99,7 @@ namespace Extenders { if (newStatus != 0) { if ((newStatus & 2) != 0) { _status = 0; - break; + return; // Stop running } // Update config: diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp index 80f5cba54..9212e84de 100644 --- a/FluidNC/test/Extender/I2CExtenderTests.cpp +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -10,6 +10,7 @@ #include "Capture.h" #include +#include namespace { struct GPIONative { @@ -27,6 +28,158 @@ namespace { inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); } inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); } }; + + class PCA9539Emulator { + uint8_t reg_config[2]; + uint8_t reg_invert[2]; + uint8_t reg_input[2]; + uint8_t reg_output[2]; + uint8_t pad_value[2]; // input values + + uint8_t accessedRegisters = 0; + uint8_t previousRegisters = 0; // For debugging purposes. + + int isrPin_; + + void setRegister(int reg, uint8_t value) { + accessedRegisters |= uint8_t(1 << reg); + switch (reg) { + // input + case 2: + reg_output[0] = value; + break; + case 3: + reg_output[1] = value; + break; + // invert + case 4: + reg_invert[0] = value; + reg_input[0] = reg_invert[0] ^ pad_value[0]; + break; + case 5: + reg_invert[1] = value; + reg_input[1] = reg_invert[1] ^ pad_value[1]; + break; + // config + case 6: + reg_config[0] = value; + break; + case 7: + reg_config[1] = value; + break; + default: + Assert(false, "Not supported"); + break; + } + } + + void handler(TwoWire* theWire, std::vector& data) { + if (data.size() == 1) { + if (data[0] == 0 || data[0] == 1) { + accessedRegisters |= uint8_t(1 << data[0]); + Assert(theWire->SendSize() == 0); + theWire->Send(uint8_t(reg_input[data[0]])); + data.clear(); + + // Clear ISR: + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, true); + } + } else if (data[0] >= 2 && data[0] <= 7) { + // ignore until next roundtrip + } else { + Assert(false, "Unknown register"); + } + } else if (data.size() == 2) { + if (data[0] >= 2 && data[0] <= 7) { + setRegister(data[0], data[1]); + data.clear(); + } else { + Assert(false, "Unknown register"); + } + } else { + Assert(false, "Unknown size"); + } + } + + public: + PCA9539Emulator(int isrPin) : isrPin_(isrPin) { + for (int i = 0; i < 2; ++i) { + reg_config[i] = 0; + reg_invert[i] = 0; + reg_input[i] = 0; + reg_output[i] = 0; + pad_value[i] = 0; + } + + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, true); + } + } + + static void wireResponseHandler(TwoWire* theWire, std::vector& data, void* userData) { + static_cast(userData)->handler(theWire, data); + } + + void setPadValue(int pinId, bool v) { + uint8_t mask = uint8_t(1 << (pinId % 8)); + int idx = pinId / 8; + + if (reg_config[idx] & mask) // input + { + auto oldValue = pad_value[idx] & mask; + auto newValue = v ? mask : uint8_t(0); + + if (oldValue != newValue) { + pad_value[idx] = (pad_value[idx] & ~mask) | newValue; + reg_input[idx] = reg_invert[idx] ^ pad_value[idx]; + + // Trigger ISR on 'falling'. + if (isrPin_ >= 0) { + GPIONative::write(isrPin_, false); + } + } + } + } + + bool getPadValue(int pinId) { + uint8_t mask = uint8_t(1 << (pinId % 8)); + int idx = pinId / 8; + + if ((reg_config[idx] & mask) == 0) { + // This is an output pin, so combine registers: + return ((reg_output[idx] ^ reg_invert[idx]) & mask) != 0; + } else { + // This is an input pin, so use the pad_value + return (pad_value[idx] & mask) != 0; + } + } + + uint8_t registersUsed() { + auto result = accessedRegisters; + previousRegisters = result; + accessedRegisters = 0; + return result; + } + }; + + class Roundtrip { + uint32_t before; + + public: + Roundtrip() { before = Capture::instance().current(); } + + ~Roundtrip() { + for (int i = 0; i < 3; ++i) { + while (Capture::instance().current() < before + 1) { + delay(10); + } + before = Capture::instance().current(); + } + } + }; + + std::mutex single_thread; } namespace Configuration { @@ -104,6 +257,10 @@ namespace Configuration { }; Test(I2CExtender, InitDeinit) { + std::lock_guard guard(single_thread); + + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -117,6 +274,9 @@ namespace Configuration { mconfig._i2c = &bus; config = &mconfig; + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + // Setup the extender Extenders::I2CExtender i2c; FakeInitHandler fakeInit(false); @@ -126,6 +286,9 @@ namespace Configuration { } Test(I2CExtender, ClaimRelease) { + std::lock_guard guard(single_thread); + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -139,6 +302,9 @@ namespace Configuration { mconfig._i2c = &bus; config = &mconfig; + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + // Setup the extender Extenders::I2CExtender i2c; FakeInitHandler fakeInit(false); @@ -161,23 +327,10 @@ namespace Configuration { i2c.free(2); } - class Roundtrip { - uint32_t before; - - public: - Roundtrip() { before = Capture::instance().current(); } - - ~Roundtrip() { - for (int i = 0; i < 10; ++i) { - while (Capture::instance().current() < before + 1) { - delay(10); - } - before = Capture::instance().current(); - } - } - }; - Test(I2CExtender, ExtenderNoInterrupt) { + std::lock_guard guard(single_thread); + PCA9539Emulator pca(-1); + // Initialize I2C bus Machine::I2CBus bus; bus._sda = Pin::create("gpio.16"); @@ -192,6 +345,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -200,25 +354,8 @@ namespace Configuration { i2c.validate(); i2c.init(); - // Expected register values (see datasheet): - // - // 4 invert - // 1 = invert, 0 = normal - // - // 6 config - // 1 = input, 0 = output - // - // 2 write - // 1 = high, 0 = low - // - // 0 read - // high = 1, low = 0 - { // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x01); i2c.claim(0); i2c.setupPin(0, Pin::Attr::Output); @@ -226,24 +363,17 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), - uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + // Check PCA values: + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(!pca.getPadValue(0)); } // Read will trigger an update, because we don't have an ISR { - Wire.Send(0x01); bool readPin = i2c.readPin(0); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 1, "Expected single data request / response roundtrip, got %d", int(recv.size())); - Assert(recv[0] == 0, "Expected read"); - Assert(readPin == true, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x01, "Expected roundtrip for read / no ISR"); + Assert(readPin == false, "Expected 'true' on pin"); } // Test write pin: @@ -252,28 +382,24 @@ namespace Configuration { i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(pca.getPadValue(0), "Expected pad to be 'true'"); } { // Write to set it 'low'. i2c.writePin(0, false); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 0, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(!pca.getPadValue(0), "Expected pad to be 'false'"); } { // Write to set it 'low'. It's already low, no-op. i2c.writePin(0, false); i2c.flushWrites(); // no-op. + Assert(pca.registersUsed() == 0x00, "Expected roundtrip for write / no ISR"); + Assert(!pca.getPadValue(0), "Expected pad to be 'false'"); } { // Write to set it 'high'. @@ -282,82 +408,83 @@ namespace Configuration { { Roundtrip rt; } auto recv = Wire.Receive(); - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); + Assert(pca.registersUsed() == 0x04, "Expected roundtrip for write / no ISR"); + Assert(pca.getPadValue(0), "Expected pad to be 'false'"); } // NOTE: We ended with setting pin #0 to 'high' = 0x01 // Setup pin for reading: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - i2c.claim(1); i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), - uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(pca.getPadValue(0)); + Assert(!pca.getPadValue(1)); } // Setup another pin for reading with an invert mask and a PU: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x04); - i2c.claim(2); i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), - uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); + Assert(pca.getPadValue(0)); + Assert(!pca.getPadValue(1)); + Assert(!pca.getPadValue(2)); } // Test read pin: { - Wire.Send(0x02); bool readPin = i2c.readPin(1); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); - Assert(recv[0] == 0, "Expected read"); + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); + Assert(readPin == false); + } + + // Test read pin: + { + bool readPin = i2c.readPin(2); + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); Assert(readPin == true, "Expected 'true' on pin"); } + pca.setPadValue(1, true); + pca.setPadValue(2, true); + + // Test read pin: + { + bool readPin = i2c.readPin(1); + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); + Assert(readPin == true); + } + // Test read pin: { - Wire.Send(0x02); bool readPin = i2c.readPin(2); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1, "Expected single data request / response roundtrip"); - Assert(recv[0] == 0, "Expected read"); + Assert(pca.registersUsed() == 0x01, "Expected invert, config, write, read bytes being used"); Assert(readPin == false, "Expected 'true' on pin"); } } Test(I2CExtender, ExtenderWithInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -373,6 +500,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender with ISR on gpio.15 Extenders::I2CExtender i2c; @@ -381,48 +509,19 @@ namespace Configuration { i2c.validate(); i2c.init(); - // Expected register values (see datasheet): - // - // 4 invert - // 1 = invert, 0 = normal - // - // 6 config - // 1 = input, 0 = output - // - // 2 write - // 1 = high, 0 = low - // - // 0 read - // high = 1, low = 0 - { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x01); - i2c.claim(0); i2c.setupPin(0, Pin::Attr::Output); { Roundtrip rt; } - // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x00), - uint8_t(0x02), uint8_t(0x00), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); + Assert(pca.registersUsed() == 0x55, "Expected invert, config, write, read bytes being used"); } // Read will NOT trigger an update because we have an ISR to tell us when it changes: { bool readPin = i2c.readPin(0); - auto recv = Wire.Receive(); - - Assert(recv.size() == 0, "Expected single data request / response roundtrip, got %d", int(recv.size())); - Assert(readPin == true, "Expected 'true' on pin"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(readPin == false, "Expected 'false' on pin"); + Assert(pca.registersUsed() == 0, "Expected no-op for read"); } // Test write pin: @@ -431,24 +530,14 @@ namespace Configuration { i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04, "Expected no-op for read"); } { // Write to set it 'low'. i2c.writePin(0, false); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 0, "Expected write reg 0 = 0"); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04); } { // Write to set it 'low'. It's already low, no-op. @@ -456,141 +545,83 @@ namespace Configuration { i2c.flushWrites(); // no-op. - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0, "Expected no-op"); } { // Write to set it 'high'. i2c.writePin(0, true); i2c.flushWrites(); { Roundtrip rt; } - auto recv = Wire.Receive(); - - Assert(recv.size() == 2, "Expected single data request / response roundtrip"); - Assert(recv[0] == 2, "Expected write reg 0"); - Assert(recv[1] == 1, "Expected write reg 0 = 0"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x04); } // NOTE: We ended with setting pin #0 to 'high' = 0x01 // Setup pin for reading: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - i2c.claim(1); i2c.setupPin(1, Pin::Attr::Input); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x06), uint8_t(0x02), - uint8_t(0x02), uint8_t(0x01), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x55); } // Setup another pin for reading with an invert mask and a PU: { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x04); - i2c.claim(2); i2c.setupPin(2, Pin::Attr::Input | Pin::Attr::ActiveLow | Pin::Attr::PullUp); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 + 1, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[7] = { uint8_t(0x04), uint8_t(0x04), uint8_t(0x06), uint8_t(0x06), - uint8_t(0x02), uint8_t(0x05), uint8_t(0x00) }; - Assert(!memcmp(expected, buffer.data(), 7), "Didn't expect data"); - - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x55); } // Test read pin: { bool readPin = i2c.readPin(1); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + { Roundtrip rt; } + Assert(pca.registersUsed() == 0x0); Assert(readPin == false, "Expected 'true' on pin"); } // Test read pin: { bool readPin = i2c.readPin(2); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + { Roundtrip rt; } + Assert(pca.registersUsed() == 0x0); Assert(readPin == true, "Expected 'true' on pin"); } // Trigger an ISR, change both pins { - Wire.Send(0x02); - GPIONative::write(15, true); - GPIONative::write(15, false); + pca.setPadValue(1, true); + pca.setPadValue(2, true); { Roundtrip rt; } - auto recv = Wire.Receive(); - Assert(recv.size() == 1); - Assert(recv[0] == 0); + Assert(pca.registersUsed() == 0x01); } // Test read pin: { bool readPin = i2c.readPin(1); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x0); Assert(readPin == true, "Expected 'true' on pin"); } // Test read pin: { bool readPin = i2c.readPin(2); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); + Assert(pca.registersUsed() == 0x0); Assert(readPin == false, "Expected 'true' on pin"); } } void HandleInterrupt(void* data) { ++(*reinterpret_cast(data)); } - volatile uint16_t currentInput = 0; - void WireResponseHandler(TwoWire* theWire, std::vector& data) { - if (data.size() == 1) { - if (data[0] == 0) { - Assert(theWire->SendSize() == 0); - theWire->Send(uint8_t(currentInput)); - data.clear(); - } else if (data[0] == 1) { - Assert(theWire->SendSize() == 0); - theWire->Send(uint8_t(currentInput >> 8)); - data.clear(); - } else if (data[0] >= 2 && data[0] <= 7) { - // ignore until next roundtrip - } else { - Assert(false, "Unknown register"); - } - } else if (data.size() == 2) { - if (data[0] >= 2 && data[0] <= 7) { - data.clear(); - } else { - Assert(false, "Unknown register"); - } - } else { - Assert(false, "Unknown size"); - } - } - Test(I2CExtender, ISRTriggerWithInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -606,6 +637,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -615,45 +647,24 @@ namespace Configuration { i2c.init(); { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - Wire.Send(0x00); - i2c.claim(9); i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } uint32_t isrCounter = 0; { - Wire.Send(0x00); - Wire.Send(0x00); - i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } { Roundtrip rt; } @@ -661,38 +672,49 @@ namespace Configuration { // Test read pin: { bool readPin = i2c.readPin(9); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); Assert(readPin == false, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x00); } // Change state, wait till roundtrip { - Wire.Send(0x00); - Wire.Send(0x02); - - // Trigger ISR pin 'falling' - GPIONative::write(15, true); - GPIONative::write(15, false); - { Roundtrip rt; } - - auto recv = Wire.Receive(); - Assert(recv.size() == 2); - Assert(recv[0] == 0); - Assert(recv[1] == 1); + pca.setPadValue(9, true); { Roundtrip rt; } // Test if ISR update went correctly: Assert(isrCounter == 1); + Assert(pca.registersUsed() == 0x03); // Test read pin: bool readPin = i2c.readPin(9); - Assert(Wire.ReceiveSize() == 0, "Expected empty receive buffer"); Assert(readPin == true, "Expected 'true' on pin"); + Assert(pca.registersUsed() == 0x00); + } + + { + i2c.detachInterrupt(9); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0xFF); + } + + // Change state, wait till roundtrip + { + pca.setPadValue(9, false); + { Roundtrip rt; } + + // Test if ISR detach went correctly: + Assert(isrCounter == 1); + Assert(pca.registersUsed() == 0x03); } } Test(I2CExtender, ISRTriggerWithoutInterrupt) { + std::lock_guard guard(single_thread); GPIONative::initialize(); + PCA9539Emulator pca(15); // Initialize I2C bus Machine::I2CBus bus; @@ -708,6 +730,7 @@ namespace Configuration { config = &mconfig; Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); // Setup the extender Extenders::I2CExtender i2c; @@ -717,35 +740,19 @@ namespace Configuration { i2c.init(); { - // Setup will trigger some events on I2C: 'config', 'invert', 'write', 'read'. - // We have to set the 'read' before setting up the pin, or the I2C comms are going to fail. - // Let's just set it 'high': - Wire.Send(0x00); - Wire.Send(0x00); - i2c.claim(9); i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - auto buffer = Wire.Receive(); - Assert(buffer.size() == 3 * 2 * 2 + 2, "Expected invert, config, write, read bytes being sent"); - - const uint8_t expected[14] = { uint8_t(0x04), uint8_t(0x00), uint8_t(0x05), uint8_t(0x00), uint8_t(0x06), - uint8_t(0x00), uint8_t(0x07), uint8_t(0x02), uint8_t(0x02), uint8_t(0x00), - uint8_t(0x03), uint8_t(0x00), uint8_t(0x00), uint8_t(0x01) }; - Assert(!memcmp(expected, buffer.data(), 14), "Unexpected data"); + Assert(pca.registersUsed() == 0xFF); } uint32_t isrCounter = 0; { // From this point on, we just need to respond to wire requests - currentInput = 0x0000; - Wire.Clear(); - Wire.SetResponseHandler(WireResponseHandler); - i2c.attachInterrupt(9, HandleInterrupt, &isrCounter, CHANGE); } @@ -757,7 +764,8 @@ namespace Configuration { // Change state, wait till roundtrip { - currentInput = 0x0200; + pca.setPadValue(9, true); + { Roundtrip rt; } // Test if ISR update went correctly: @@ -766,17 +774,37 @@ namespace Configuration { // Test read pin: bool readPin = i2c.readPin(9); Assert(readPin == true, "Expected 'true' on pin"); + } + + { + pca.setPadValue(9, false); { Roundtrip rt; } // Test if ISR update went correctly: - Assert(isrCounter == 1); + Assert(isrCounter == 2); // Test read pin: bool readPin2 = i2c.readPin(9); - Assert(readPin2 == true, "Expected 'true' on pin"); + Assert(readPin2 == false, "Expected 'false' on pin"); } - Wire.Clear(); + { + i2c.detachInterrupt(9); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + Assert(pca.registersUsed() == 0xFF); + } + + // Change state, wait till roundtrip + { + pca.setPadValue(9, false); + { Roundtrip rt; } + + // Test if ISR detach went correctly: + Assert(isrCounter == 2); + } } } diff --git a/X86TestSupport/TestSupport/Wire.cpp b/X86TestSupport/TestSupport/Wire.cpp index 2ada77bbe..35ff5cdad 100644 --- a/X86TestSupport/TestSupport/Wire.cpp +++ b/X86TestSupport/TestSupport/Wire.cpp @@ -28,7 +28,8 @@ void TwoWire::Clear() { std::lock_guard guard(mut); sentData.clear(); receivedData.clear(); - handler = nullptr; + handler = nullptr; + handlerUserData = nullptr; } // TwoWire interface: @@ -127,7 +128,7 @@ size_t TwoWire::write(uint8_t ch) { } if (handler) { - (*handler)(this, sentData); + (*handler)(this, sentData, handlerUserData); } return 0; } diff --git a/X86TestSupport/TestSupport/Wire.h b/X86TestSupport/TestSupport/Wire.h index b28f807ca..866597748 100644 --- a/X86TestSupport/TestSupport/Wire.h +++ b/X86TestSupport/TestSupport/Wire.h @@ -14,7 +14,8 @@ class TwoWire : public Stream { std::vector sentData; std::mutex mut; - using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data); + using ResponseHandler = void (*)(TwoWire* theWire, std::vector& data, void* userData); + void* handlerUserData; ResponseHandler handler; public: @@ -28,7 +29,10 @@ class TwoWire : public Stream { std::vector Receive(); size_t ReceiveSize() { return sentData.size(); } void Clear(); - void SetResponseHandler(ResponseHandler handler) { this->handler = handler; } + void SetResponseHandler(ResponseHandler handler, void* userData) { + this->handlerUserData = userData; + this->handler = handler; + } // TwoWire interface: From 07bfceac45f11be8ff6f094388cd9f4578133080 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Mar 2022 15:42:31 +0100 Subject: [PATCH 086/100] Fixed a few bugs in the pin extender --- FluidNC/src/Extenders/I2CExtender.cpp | 45 +++++++++----- FluidNC/src/Machine/I2CBus.cpp | 9 ++- FluidNC/src/Machine/LimitPin.cpp | 4 +- FluidNC/src/Machine/Motor.cpp | 8 +++ FluidNC/src/Machine/Motor.h | 2 +- FluidNC/test/Extender/I2CExtenderTests.cpp | 69 ++++++++++++++++++++-- 6 files changed, 110 insertions(+), 27 deletions(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index fa7b2bbf8..d0bde9d96 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -20,9 +20,6 @@ namespace Extenders { uint8_t I2CExtender::I2CGetValue(uint8_t address, uint8_t reg) { auto bus = _i2cBus; - // First make sure the bus is empty, so we read that which we have written: - while (bus->read(address, ®, 1) != 0) {} - int err; if ((err = bus->write(address, ®, 1)) != 0) { log_warn("Cannot read from I2C bus: " << Machine::I2CBus::ErrorDescription(err)); @@ -37,6 +34,8 @@ namespace Extenders { IOError(); } else { + // This log line will probably generate a stack overflow and way too much data. Use with care: + // log_info("Request address: " << int(address) << ", reg: " << int(reg) << " gives: " << int(result)); errorCount = 0; } return result; @@ -56,6 +55,8 @@ namespace Extenders { log_warn("Cannot write to I2C bus: " << Machine::I2CBus::ErrorDescription(err)); IOError(); } else { + // This log line will probably generate a stack overflow and way too much data. Use with care: + // log_info("Set address: " << int(address) << ", reg: " << int(reg) << " to: " << int(value)); errorCount = 0; } } @@ -188,12 +189,27 @@ namespace Extenders { // If we don't have an ISR, we must update everything. Otherwise, we can cherry pick: bool handleInvertSoftware = (_invertReg == 0xFF); + uint8_t newBytes[8]; for (int i = 0; i < claimedValues; ++i) { - auto oldByte = _input.bytes[i]; auto newByte = I2CGetValue(address, currentRegister); if (handleInvertSoftware) { newByte ^= _invert.bytes[i]; } + newBytes[i] = newByte; + + currentRegister++; + if (currentRegister == registersPerDevice + _inputReg) { + ++address; + } + } + + // Remove the busy flag, keep the rest. If we don't do that here, we + // end up with a race condition if we use _status in the ISR. + _status &= ~0x10; + + for (int i = 0; i < claimedValues; ++i) { + auto oldByte = _input.bytes[i]; + auto newByte = newBytes[i]; if (oldByte != newByte) { // Handle ISR's: @@ -206,16 +222,11 @@ namespace Extenders { auto o = (oldByte & mask); auto n = (newByte & mask); if (o != n) { - isr.callback(isr.data); + isr.callback(isr.data); // bug; race condition } } } } - - currentRegister++; - if (currentRegister == registersPerDevice + _inputReg) { - ++address; - } } } } @@ -288,11 +299,11 @@ namespace Extenders { // Ensure data is available: std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); - xTaskCreatePinnedToCore(isrTaskLoop, // task - "i2cHandler", // name for task - configMINIMAL_STACK_SIZE + 512, // size of task stack - this, // parameters - 1, // priority + xTaskCreatePinnedToCore(isrTaskLoop, // task + "i2cHandler", // name for task + configMINIMAL_STACK_SIZE + 512 + 2048, // size of task stack + this, // parameters + 1, // priority &_isrHandler, SUPPORT_TASK_CORE // core ); @@ -391,6 +402,8 @@ namespace Extenders { Assert(mode == CHANGE, "Only mode CHANGE is allowed for pin extender ISR's."); Assert(index < 64 && index >= 0, "Pin index out of range"); + // log_debug("Attaching interrupt (I2C) on index " << int(index)); + ISRData& data = _isrData[index]; data.callback = callback; data.data = arg; @@ -409,6 +422,8 @@ namespace Extenders { void I2CExtender::detachInterrupt(pinnum_t index) { Assert(index < 64 && index >= 0, "Pin index out of range"); + // log_debug("Detaching interrupt (I2C) on index " << int(index)); + ISRData& data = _isrData[index]; data.callback = nullptr; data.data = nullptr; diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index daf5120a2..e62b9d1de 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -77,13 +77,12 @@ namespace Machine { // log_debug("I2C read addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " // << (i2c ? "non null" : "null")); - for (size_t i = 0; i < count; ++i) { - if (i2c->requestFrom((int)address, 1) != 1) { - return i; - } + size_t c = i2c->requestFrom((int)address, count); + + for (size_t i = 0; i < c; ++i) { data[i] = i2c->read(); } - return count; + return c; } } diff --git a/FluidNC/src/Machine/LimitPin.cpp b/FluidNC/src/Machine/LimitPin.cpp index 89182dee3..56e06e858 100644 --- a/FluidNC/src/Machine/LimitPin.cpp +++ b/FluidNC/src/Machine/LimitPin.cpp @@ -45,6 +45,7 @@ namespace Machine { void IRAM_ATTR LimitPin::handleISR() { read(); + if (sys.state != State::Alarm && sys.state != State::ConfigAlarm && sys.state != State::Homing) { if (_pHardLimits && rtAlarm == ExecAlarm::None) { #if 0 @@ -57,7 +58,7 @@ namespace Machine { } #endif - // log_debug("Hard limits"); // This might not work from ISR context + // log_debug("Hard limits"); // This might not work from ISR context mc_reset(); // Initiate system kill. rtAlarm = ExecAlarm::HardLimit; // Indicate hard limit critical event } @@ -91,6 +92,7 @@ namespace Machine { if (_pin.undefined()) { return; } + set_bitnum(Axes::limitMask, _axis); _pin.report(_legend.c_str()); auto attr = Pin::Attr::Input | Pin::Attr::ISR; diff --git a/FluidNC/src/Machine/Motor.cpp b/FluidNC/src/Machine/Motor.cpp index c86009b5c..c965a0e42 100644 --- a/FluidNC/src/Machine/Motor.cpp +++ b/FluidNC/src/Machine/Motor.cpp @@ -10,6 +10,8 @@ #include "Axes.h" namespace Machine { + Motor::Motor(int axis, int motorNum) : _axis(axis), _motorNum(motorNum) {} + void Motor::group(Configuration::HandlerBase& handler) { handler.item("limit_neg_pin", _negPin); handler.item("limit_pos_pin", _posPin); @@ -26,6 +28,12 @@ namespace Machine { } void Motor::init() { + log_debug("Initializing motor / limits..."); + + _negLimitPin = new LimitPin(_negPin, _axis, _motorNum, -1, _hardLimits); + _posLimitPin = new LimitPin(_posPin, _axis, _motorNum, 1, _hardLimits); + _allLimitPin = new LimitPin(_allPin, _axis, _motorNum, 0, _hardLimits); + if (strcmp(_driver->name(), "null_motor") != 0) { set_bitnum(Machine::Axes::motorMask, Machine::Axes::motor_bit(_axis, _motorNum)); } diff --git a/FluidNC/src/Machine/Motor.h b/FluidNC/src/Machine/Motor.h index 90674fd45..fd78401cf 100644 --- a/FluidNC/src/Machine/Motor.h +++ b/FluidNC/src/Machine/Motor.h @@ -25,7 +25,7 @@ namespace Machine { int _motorNum; public: - Motor(int axis, int motorNum) : _axis(axis), _motorNum(motorNum) {} + Motor(int axis, int motorNum); MotorDrivers::MotorDriver* _driver = nullptr; float _pulloff = 1.0f; // mm diff --git a/FluidNC/test/Extender/I2CExtenderTests.cpp b/FluidNC/test/Extender/I2CExtenderTests.cpp index 9212e84de..95e6f8dbb 100644 --- a/FluidNC/test/Extender/I2CExtenderTests.cpp +++ b/FluidNC/test/Extender/I2CExtenderTests.cpp @@ -653,7 +653,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } uint32_t isrCounter = 0; @@ -664,7 +665,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } { Roundtrip rt; } @@ -697,7 +699,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } // Change state, wait till roundtrip @@ -746,7 +749,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } uint32_t isrCounter = 0; @@ -795,7 +799,8 @@ namespace Configuration { // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: { Roundtrip rt; } - Assert(pca.registersUsed() == 0xFF); + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); } // Change state, wait till roundtrip @@ -807,4 +812,58 @@ namespace Configuration { Assert(isrCounter == 2); } } + + void ReadInISRHandler(void* data) { + auto i2c = static_cast(data); + auto value = i2c->readPin(9); + Assert(value == true); + } + + Test(I2CExtender, ReadInISR) { + std::lock_guard guard(single_thread); + GPIONative::initialize(); + PCA9539Emulator pca(15); + + // Initialize I2C bus + Machine::I2CBus bus; + bus._sda = Pin::create("gpio.16"); + bus._scl = Pin::create("gpio.17"); + bus._frequency = 100000; + bus._busNumber = 0; + bus.init(); + + // We need to set up the I2C config in the global 'config', or init of the extender will fail. + Machine::MachineConfig mconfig; + mconfig._i2c = &bus; + config = &mconfig; + + Wire.Clear(); + Wire.SetResponseHandler(PCA9539Emulator::wireResponseHandler, &pca); + + // Setup the extender + Extenders::I2CExtender i2c; + FakeInitHandler fakeInit(false); + i2c.group(fakeInit); + i2c.validate(); + i2c.init(); + + { + i2c.claim(9); + i2c.setupPin(9, Pin::Attr::Input | Pin::Attr::ISR); + + // Wait until synced (should be immediate after the thread gets some cpu) and check I2C comms: + { Roundtrip rt; } + + auto regUsed = pca.registersUsed(); + Assert(regUsed >= 0xfd && regUsed <= 0xFF); + } + + { + pca.setPadValue(9, false); + i2c.attachInterrupt(9, ReadInISRHandler, &i2c, CHANGE); + pca.setPadValue(9, true); + i2c.detachInterrupt(9); + pca.setPadValue(9, false); + } + } } From 6c72bfef986c0670c133a232fcb88d521c4e839b Mon Sep 17 00:00:00 2001 From: bdring Date: Sat, 16 Apr 2022 14:26:59 -0500 Subject: [PATCH 087/100] WIP --- FluidNC/src/Extenders/I2CExtender.cpp | 14 +++++++++++++- FluidNC/src/Extenders/I2CExtender.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/FluidNC/src/Extenders/I2CExtender.cpp b/FluidNC/src/Extenders/I2CExtender.cpp index d0bde9d96..cc136c0e0 100644 --- a/FluidNC/src/Extenders/I2CExtender.cpp +++ b/FluidNC/src/Extenders/I2CExtender.cpp @@ -13,7 +13,9 @@ #include namespace Extenders { - EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, EnumItem(int(I2CExtenderDevice::Unknown)) }; + EnumItem i2cDevice[] = { { int(I2CExtenderDevice::PCA9539), "pca9539" }, + { int(I2CExtenderDevice::PCA9555), "pca9555" }, + EnumItem(int(I2CExtenderDevice::Unknown)) }; I2CExtender::I2CExtender() : _i2cBus(nullptr), _usedIORegisters(0), _dirtyWriteBuffer(0), _dirtyWrite(0), _status(0) {} @@ -291,6 +293,16 @@ namespace Extenders { _operationReg = 6; break; + case I2CExtenderDevice::PCA9555: + // See data sheet page 7+: + _address = 0x20 + _deviceId; + _ports = 16; + _inputReg = 0; + _outputReg = 2; + _invertReg = 4; + _operationReg = 6; + break; + default: Assert(false, "Pin extender device is not supported!"); break; diff --git a/FluidNC/src/Extenders/I2CExtender.h b/FluidNC/src/Extenders/I2CExtender.h index d5bacc1b9..74ebb2f75 100644 --- a/FluidNC/src/Extenders/I2CExtender.h +++ b/FluidNC/src/Extenders/I2CExtender.h @@ -13,12 +13,14 @@ namespace Pins { class PCA9539PinDetail; + class PCA9555PinDetail; } namespace Extenders { enum class I2CExtenderDevice { Unknown, PCA9539, + PCA9555, }; // Pin extenders... From bcb8295c170897b698d6062ab8f28c2da4302a82 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Sat, 5 Feb 2022 22:41:11 +0100 Subject: [PATCH 088/100] Fixed ISR mess the nasty way. (#273) Co-authored-by: Stefan de Bruijn --- FluidNC/src/ControlPin.cpp | 11 ++++++++++- FluidNC/src/ControlPin.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/FluidNC/src/ControlPin.cpp b/FluidNC/src/ControlPin.cpp index bdb47127b..5acfe7c7d 100644 --- a/FluidNC/src/ControlPin.cpp +++ b/FluidNC/src/ControlPin.cpp @@ -33,7 +33,16 @@ void ControlPin::init() { _pin.setAttr(attr); _pin.attachInterrupt(ISRHandler, CHANGE, this); _rtVariable = false; - _value = _pin.read(); + _value = _pin.read(); + // Control pins must start in inactive state + if (_value) { + log_error(_legend << " pin is active at startup"); + rtAlarm = ExecAlarm::ControlPin; + } +} + +String ControlPin::report() { + return get() ? String(_letter) : String(""); } ControlPin::~ControlPin() { diff --git a/FluidNC/src/ControlPin.h b/FluidNC/src/ControlPin.h index e5693d651..459c0465e 100644 --- a/FluidNC/src/ControlPin.h +++ b/FluidNC/src/ControlPin.h @@ -14,6 +14,8 @@ class ControlPin { // Interval during which we ignore repeated control pin activations const int debounceUs = 10000; // 10000 us = 10 ms + void IRAM_ATTR handleISR(); + CreateISRHandlerFor(ControlPin, handleISR); public: const char* _legend; // The name that appears in init() messages and the name of the configuration item From 5120353ea5d1dde1aab7db02b39441eedfe5dc34 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 4 Feb 2022 15:44:20 +0100 Subject: [PATCH 089/100] python3 generic install scripts --- .../linux-python3/HOWTO-INSTALL.txt | 119 ++++++++++++++++++ .../linux-python3/checksecurity.sh | 19 +++ install_scripts/linux-python3/fluidterm.sh | 8 ++ install_scripts/linux-python3/install-bt.sh | 19 +++ install_scripts/linux-python3/install-fs.sh | 14 +++ install_scripts/linux-python3/install-wifi.sh | 19 +++ install_scripts/linux-python3/tools.sh | 30 +++++ 7 files changed, 228 insertions(+) create mode 100644 install_scripts/linux-python3/HOWTO-INSTALL.txt create mode 100755 install_scripts/linux-python3/checksecurity.sh create mode 100755 install_scripts/linux-python3/fluidterm.sh create mode 100755 install_scripts/linux-python3/install-bt.sh create mode 100755 install_scripts/linux-python3/install-fs.sh create mode 100755 install_scripts/linux-python3/install-wifi.sh create mode 100755 install_scripts/linux-python3/tools.sh diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux-python3/HOWTO-INSTALL.txt new file mode 100644 index 000000000..ea1533442 --- /dev/null +++ b/install_scripts/linux-python3/HOWTO-INSTALL.txt @@ -0,0 +1,119 @@ +## Installing FluidNC on your ESP32 + +_Please note: These instructions apply to the case where you have +downloaded a release bundle from +https://github.com/bdring/FluidNC/releases - a zip file named +fluidnc-vN.N.N-linux-python3.zip . They do not apply to installing from +a source tree. This HOWTO file is present in the source tree so that +it can be packed into a release bundle by automated release scripting, +but it does not tell you how to install from source. For that, visit +https://github.com/bdring/FluidNC/wiki/FluidNC-Compiling#use-vs-code--platformio-to-compile +_ + +These install scripts use Python3 and the Python version of esptool. +https://docs.espressif.com/projects/esptool/en/latest/esp32/ +They are suitable for any system supporting esptool.py +eg. non-intel (arm) linux systems, such as Raspberry PI's, or +older 32-bit intel (x86) based systems + +Most Linux (and other) distributions will already have Python3 +preinstalled. If you have problems, search the web for: +"install python3 on " +replacing with the name of your operating system. + +The script will attempt to install esptool.py if it is not already +present, if this fails please see the esptool documentation and +forums for support. +https://docs.espressif.com/projects/esptool/en/latest/esp32/installation.html +https://www.esp32.com/viewforum.php?f=23 +_ + +Unpack this FluidNC release Zip file. + +Plug your ESP32 into a USB port. + +At a shell prompt, cd to the directory that contains the file +you are reading and run one of the following commands: + +To install the WiFi version: sh install-wifi.sh +To install the Bluetooth version: sh install-bt.sh +To replace the ESP32 local filesystem: sh install-fs.sh + +### Local Filesystem Considerations + +Replacing the local filesystem is only useful for the wifi version. +The Bluetooth version can start with an empty local filesystem. + +The disadvantage of replacing the local filesystem is that it will +overwrite any files that you might already have there, such as FluidNC +config files, WebUI preferences and macros. The advantage is that you +will get the latest version of the index.html.gz file that contains +the WebUI code. Another way to get a new index.html.gz is to upload +it via WebUI from wifi/index.html.gz herein. + +A good approach is to use install-fs only on your first FluidNC +installation, to start from a clean slate. + +### Running Fluidterm + +The FluidNC install scripts run Fluidterm automatically at the end, +but if you want to run it separately, you can type + + sh fluidterm.sh + +or just + + ./fluidterm.sh + +### If Fluidterm Won't Start ... + +Fluidterm is intended to be helpful but it is not absolutely necessary +for using FluidNC. Fluidterm lets you interact directly with FluidNC +over a USB serial port. There are other ways to do that, so if you +have trouble running Fluidterm, you can just ignore it. + +### Alternatives to Fluidterm + +Most GCode sender programs have some way to send commands directly to +FluidNC, but it can sometimes be helpful to bypass the complexity of +a sender and use a more direct path. Fluidterm is one such direct +path, but there are others, typically called "serial terminals". + +For Linux, there are many such programs, such as "screen", "minicom", "cu", +"picocom", and "PuTTY". The one that is most likely to be preinstalled +is named "screen". If screen is not preinstalled, you might be able to +install it with (on Ubuntu or Debian). + + sudo apt update + sudo apt install screen + +To use screen, go to a shell window and type: + + ls /dev/tty* + +That will give you a list of serial ports. You need to find the one +that is connected to FluidNC. It will probably have a name that starts +with "/dev/ttyUSB". Once you have found that name, type + + screen /dev/ttyUSBWHATEVER 115200 + +To exit from screen, type Control-A k + +Search the web for more documentation about screen, or for instructions +for installing it on other versions of Linux. + +### What Can Go Wrong? + +esptool.py is only available on Python3.6 and higher, if your system +does not support that you may be able to use the python2.7 version of +esptool.py by manually installing it first: + pip install esptool.py +The install_* scripts should then be able to complete the firmware install, +but will fail after that when they cannot run fluidterm. See above +for alternatives to fluidterm. + +If Python3.6+ is available on your system, but (for whatever reason) you +use a lower Python version as the system default you should use a +python virtual environment (venv) to do the install without needing to +modify your system-wide python environment: +https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ diff --git a/install_scripts/linux-python3/checksecurity.sh b/install_scripts/linux-python3/checksecurity.sh new file mode 100755 index 000000000..d2f0283f2 --- /dev/null +++ b/install_scripts/linux-python3/checksecurity.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +. ./tools.sh + +esptool_basic dump_mem 0x3ff5a018 4 SecurityFuses.bin + +if ! cmp -s SecurityFuses.bin common/SecurityFusesOK.bin ; then + echo ******************************************* + echo * Secure boot is enabled on this ESP32 * + echo * Loading FluidNC would probably brick it * + echo * !ABORTED! Read Wiki for more Info * + echo ******************************************* + cmp -l SecurityFuses.bin common/SecurityFusesOK.bin + rm SecurityFuses.bin + exit 1 +fi + +rm SecurityFuses.bin +exit 0 diff --git a/install_scripts/linux-python3/fluidterm.sh b/install_scripts/linux-python3/fluidterm.sh new file mode 100755 index 000000000..242112766 --- /dev/null +++ b/install_scripts/linux-python3/fluidterm.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Install dependencies if needed +python3 -m pip install -q pyserial xmodem + +# Run fluidterm +python3 fluidterm/fluidterm.py $* + diff --git a/install_scripts/linux-python3/install-bt.sh b/install_scripts/linux-python3/install-bt.sh new file mode 100755 index 000000000..c776cdb36 --- /dev/null +++ b/install_scripts/linux-python3/install-bt.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if ! ./checksecurity.sh; then + exit +fi + +. ./tools.sh + +BuildType=bt + +Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootapp="0xe000 common/boot_app0.bin" +Firmware="0x10000 ${BuildType}/firmware.bin" +Partitions="0x8000 ${BuildType}/partitions.bin" + +esptool_write $Bootloader $Bootapp $Firmware $Partitions + +echo Starting fluidterm +sh fluidterm.sh diff --git a/install_scripts/linux-python3/install-fs.sh b/install_scripts/linux-python3/install-fs.sh new file mode 100755 index 000000000..74a1c0453 --- /dev/null +++ b/install_scripts/linux-python3/install-fs.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +if ! ./checksecurity.sh; then + exit +fi + +. ./tools.sh + +LocalFS="0x3d0000 wifi/spiffs.bin" + +esptool_write $LocalFS + +echo Starting fluidterm +sh fluidterm.sh diff --git a/install_scripts/linux-python3/install-wifi.sh b/install_scripts/linux-python3/install-wifi.sh new file mode 100755 index 000000000..1213d0e02 --- /dev/null +++ b/install_scripts/linux-python3/install-wifi.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if ! ./checksecurity.sh; then + exit +fi + +. ./tools.sh + +BuildType=wifi + +Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootapp="0xe000 common/boot_app0.bin" +Firmware="0x10000 ${BuildType}/firmware.bin" +Partitions="0x8000 ${BuildType}/partitions.bin" + +esptool_write $Bootloader $Bootapp $Firmware $Partitions + +echo Starting fluidterm +sh fluidterm.sh diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh new file mode 100755 index 000000000..2e98d6c18 --- /dev/null +++ b/install_scripts/linux-python3/tools.sh @@ -0,0 +1,30 @@ +# Subroutines to call esptool with common arguments + +# Install esptool.py if needed +which esptool.py +if test "$?" != "0"; then + echo esptool.py not found, attempting to install + python3 -m pip install esptool + if test "$?" != "0"; then + echo esptool.py install failed + exit 1 + fi +fi + +EsptoolPath=esptool.py + +BaseArgs="--chip esp32 --baud 230400" + +SetupArgs="--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect" + +esptool_basic () { + echo echo $EsptoolPath $BaseArgs $* + $EsptoolPath $BaseArgs $BaseArgs $* + if test "$?" != "0"; then + echo esptool.py failed + exit 1 + fi +} +esptool_write () { + esptool_basic $SetupArgs $* +} From 42fb37f44bb9714cf32f3ab111a65e09a61145e3 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 4 Feb 2022 15:46:06 +0100 Subject: [PATCH 090/100] permissions as per other installers --- install_scripts/linux-python3/checksecurity.sh | 0 install_scripts/linux-python3/fluidterm.sh | 0 install_scripts/linux-python3/install-bt.sh | 0 install_scripts/linux-python3/install-fs.sh | 0 install_scripts/linux-python3/install-wifi.sh | 0 install_scripts/linux-python3/tools.sh | 0 6 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 install_scripts/linux-python3/checksecurity.sh mode change 100755 => 100644 install_scripts/linux-python3/fluidterm.sh mode change 100755 => 100644 install_scripts/linux-python3/install-bt.sh mode change 100755 => 100644 install_scripts/linux-python3/install-fs.sh mode change 100755 => 100644 install_scripts/linux-python3/install-wifi.sh mode change 100755 => 100644 install_scripts/linux-python3/tools.sh diff --git a/install_scripts/linux-python3/checksecurity.sh b/install_scripts/linux-python3/checksecurity.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/fluidterm.sh b/install_scripts/linux-python3/fluidterm.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/install-bt.sh b/install_scripts/linux-python3/install-bt.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/install-fs.sh b/install_scripts/linux-python3/install-fs.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/install-wifi.sh b/install_scripts/linux-python3/install-wifi.sh old mode 100755 new mode 100644 diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh old mode 100755 new mode 100644 From 3c3bbf5e731080a491bbabdd740e830150d80553 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 4 Feb 2022 21:21:00 +0100 Subject: [PATCH 091/100] check for correct path on older py3 installs --- install_scripts/linux-python3/HOWTO-INSTALL.txt | 13 ++++++++++++- install_scripts/linux-python3/tools.sh | 14 +++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux-python3/HOWTO-INSTALL.txt index ea1533442..983c408e5 100644 --- a/install_scripts/linux-python3/HOWTO-INSTALL.txt +++ b/install_scripts/linux-python3/HOWTO-INSTALL.txt @@ -72,6 +72,17 @@ for using FluidNC. Fluidterm lets you interact directly with FluidNC over a USB serial port. There are other ways to do that, so if you have trouble running Fluidterm, you can just ignore it. +On headless (no GUI installed) machines fluidterm may fail with: + + ModuleNotFoundError: No module named 'tkinter' + +In this case you will need to install the correct system package for tkinter; +- note that trying to install via pip/pypi is usually not sufficient. + + Debian/Raspbian: $ sudo apt install python3-tk + Fedora/RHEL: $ sudo dnf install python3-tkinter +etc.. + ### Alternatives to Fluidterm Most GCode sender programs have some way to send commands directly to @@ -113,7 +124,7 @@ but will fail after that when they cannot run fluidterm. See above for alternatives to fluidterm. If Python3.6+ is available on your system, but (for whatever reason) you -use a lower Python version as the system default you should use a +use a lower Python version as the system default you can use a python virtual environment (venv) to do the install without needing to modify your system-wide python environment: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh index 2e98d6c18..56ba61356 100644 --- a/install_scripts/linux-python3/tools.sh +++ b/install_scripts/linux-python3/tools.sh @@ -1,7 +1,13 @@ # Subroutines to call esptool with common arguments +# Ensure local binary path is in $PATH (pip3 default target path) +echo "$PATH" | grep '$HOME\/\.local\/bin' 2>&1 >/dev/null +if test "$?" != "0"; then + export PATH="$HOME/.local/bin:$PATH" +fi + # Install esptool.py if needed -which esptool.py +which esptool.py 2>&1 >/dev/null if test "$?" != "0"; then echo esptool.py not found, attempting to install python3 -m pip install esptool @@ -9,6 +15,12 @@ if test "$?" != "0"; then echo esptool.py install failed exit 1 fi + which esptool.py 2>&1 >/dev/null + if test "$?" != "0"; then + echo esptool.py claims to have installed successfully, but cannot be found in PATH + echo PATH= $PATH + exit 1 + fi fi EsptoolPath=esptool.py From 00a2b14dfd6d0af65c1e6d17870b4d05e1344516 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 4 Feb 2022 23:22:45 +0100 Subject: [PATCH 092/100] Document group and other OS considerations --- .../linux-python3/HOWTO-INSTALL.txt | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/install_scripts/linux-python3/HOWTO-INSTALL.txt b/install_scripts/linux-python3/HOWTO-INSTALL.txt index 983c408e5..a381e3b24 100644 --- a/install_scripts/linux-python3/HOWTO-INSTALL.txt +++ b/install_scripts/linux-python3/HOWTO-INSTALL.txt @@ -12,14 +12,15 @@ _ These install scripts use Python3 and the Python version of esptool. https://docs.espressif.com/projects/esptool/en/latest/esp32/ + They are suitable for any system supporting esptool.py -eg. non-intel (arm) linux systems, such as Raspberry PI's, or -older 32-bit intel (x86) based systems +eg. non-intel (arm) linux systems, such as Raspberry PI's or +legacy machines running 32bit distributions. -Most Linux (and other) distributions will already have Python3 +Most Linux distributions will already have Python3 preinstalled. If you have problems, search the web for: "install python3 on " -replacing with the name of your operating system. +replacing with the name and version of your operating system. The script will attempt to install esptool.py if it is not already present, if this fails please see the esptool documentation and @@ -39,6 +40,14 @@ To install the WiFi version: sh install-wifi.sh To install the Bluetooth version: sh install-bt.sh To replace the ESP32 local filesystem: sh install-fs.sh +### Group membership Considerations + +Your user should be a member of the `dialup` group in order to access +the USB serial device, you can use `id -a` to see your current +groups. To add yourself to the 'dialup' group you need to use: + + sudo usermod -a -G dialup + ### Local Filesystem Considerations Replacing the local filesystem is only useful for the wifi version. @@ -128,3 +137,13 @@ use a lower Python version as the system default you can use a python virtual environment (venv) to do the install without needing to modify your system-wide python environment: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/ + +### FreeBSD and other *Nix *BSD platforms + +This tool has been used successfully on FreeBSD 13 and should work +on any unix-like platform where Python3 and esptool.py are available. +However this is unsupported; and for advanced users only. +For reference: +FreeBSD(13): + User had to be added to 'dialer' group, and py-tkinter installed from + ports to enable fluidterm. From 2e7aa297e7cf6819f2b5aefadad71dfcc50713c1 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 5 Feb 2022 15:48:52 +0100 Subject: [PATCH 093/100] use pip --user install for esptool.py --- install_scripts/linux-python3/tools.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_scripts/linux-python3/tools.sh b/install_scripts/linux-python3/tools.sh index 56ba61356..e25c07362 100644 --- a/install_scripts/linux-python3/tools.sh +++ b/install_scripts/linux-python3/tools.sh @@ -10,7 +10,7 @@ fi which esptool.py 2>&1 >/dev/null if test "$?" != "0"; then echo esptool.py not found, attempting to install - python3 -m pip install esptool + python3 -m pip install --user esptool if test "$?" != "0"; then echo esptool.py install failed exit 1 From f997fea0543b661931005032d91f48902f548b52 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Feb 2022 14:46:10 +0100 Subject: [PATCH 094/100] add the readme for the source package --- install_scripts/README-ESPTOOL-SOURCE.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 install_scripts/README-ESPTOOL-SOURCE.txt diff --git a/install_scripts/README-ESPTOOL-SOURCE.txt b/install_scripts/README-ESPTOOL-SOURCE.txt new file mode 100644 index 000000000..7bfa3fa66 --- /dev/null +++ b/install_scripts/README-ESPTOOL-SOURCE.txt @@ -0,0 +1,2 @@ +The esptool source package was downloaded from the Espressif release files at +https://github.com/espressif/esptool . From 469ef7b11b2bf38b65bd2ff6d3f75b0f2a6bb5e4 Mon Sep 17 00:00:00 2001 From: Mitch Bradley Date: Mon, 14 Feb 2022 12:24:48 -1000 Subject: [PATCH 095/100] Checked in SecurityFusesOK0.bin --- install_scripts/SecurityFusesOK0.bin | Bin 0 -> 4 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 install_scripts/SecurityFusesOK0.bin diff --git a/install_scripts/SecurityFusesOK0.bin b/install_scripts/SecurityFusesOK0.bin new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 From 29e5dca8f7d16fae537617019d822d341f97c0c1 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Tue, 1 Mar 2022 11:15:33 +0100 Subject: [PATCH 096/100] Added pin extender support. Made everything compile yet again. --- FluidNC/src/Extenders/PCA9539.cpp | 271 ++++++++++++++++++++++++++++++ FluidNC/src/Extenders/PCA9539.h | 126 ++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 FluidNC/src/Extenders/PCA9539.cpp create mode 100644 FluidNC/src/Extenders/PCA9539.h diff --git a/FluidNC/src/Extenders/PCA9539.cpp b/FluidNC/src/Extenders/PCA9539.cpp new file mode 100644 index 000000000..d7f40f2a1 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include "Extenders.h" +#include "PCA9539.h" +#include "../Logging.h" + +#include +#include + +namespace Extenders { + void PCA9539::claim(pinnum_t index) { + Assert(index >= 0 && index < 16 * 4, "PCA9539 IO index should be [0-63]; %d is out of range", index); + + uint64_t mask = uint64_t(1) << index; + Assert((_claimed & mask) == 0, "PCA9539 IO port %d is already used.", index); + + _claimed |= mask; + } + + void PCA9539::free(pinnum_t index) { + uint64_t mask = uint64_t(1) << index; + _claimed &= ~mask; + } + + uint8_t PCA9539::I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg) { + auto err = bus->write(address, ®, 1); + + if (err) { + // log_info("Error writing to i2c bus. Code: " << err); + return 0; + } + + uint8_t inputData; + if (bus->read(address, &inputData, 1) != 1) { + // log_info("Error reading from i2c bus."); + } + + return inputData; + } + + void PCA9539::I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value) { + uint8_t data[2]; + data[0] = reg; + data[1] = uint8_t(value); + auto err = bus->write(address, data, 2); + + if (err) { + log_error("Error writing to i2c bus; PCA9539 failed. Code: " << err); + } + } + + void PCA9539::validate() const { + auto i2c = config->_i2c; + Assert(i2c != nullptr, "PCA9539 works through I2C, but I2C is not configured."); + } + + void PCA9539::group(Configuration::HandlerBase& handler) { + handler.item("interrupt0", _isrData[0]._pin); + handler.item("interrupt1", _isrData[1]._pin); + handler.item("interrupt2", _isrData[2]._pin); + handler.item("interrupt3", _isrData[3]._pin); + } + + void PCA9539::isrTaskLoop(void* arg) { + auto inst = static_cast(arg); + while (true) { + void* ptr; + if (xQueueReceive(inst->_isrQueue, &ptr, portMAX_DELAY)) { + ISRData* valuePtr = static_cast(ptr); + // log_info("PCA state change ISR"); + valuePtr->updateValueFromDevice(); + } + } + } + + void PCA9539::init() { + this->_i2cBus = config->_i2c; + + _isrQueue = xQueueCreate(16, sizeof(void*)); + xTaskCreatePinnedToCore(isrTaskLoop, // task + "isr_handler", // name for task + configMINIMAL_STACK_SIZE + 256, // size of task stack + this, // parameters + 1, // priority + &_isrHandler, + SUPPORT_TASK_CORE // core + ); + + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + data._address = uint8_t(0x74 + i); + data._container = this; + data._valueBase = reinterpret_cast(&_value) + i; + + // Update the value first by reading it: + data.updateValueFromDevice(); + + if (!data._pin.undefined()) { + data._pin.setAttr(Pin::Attr::ISR | Pin::Attr::Input); + + // The interrupt pin is 'active low'. So if it falls, we're interested in the new value. + data._pin.attachInterrupt(updatePCAState, FALLING, &data); + } else { + // Reset valueBase so we know it's not bound to an ISR: + data._valueBase = nullptr; + } + } + } + + void PCA9539::ISRData::updateValueFromDevice() { + const uint8_t InputReg = 0; + auto i2cBus = _container->_i2cBus; + + auto r1 = I2CGetValue(i2cBus, _address, InputReg); + auto r2 = I2CGetValue(i2cBus, _address, InputReg + 1); + uint16_t oldValue = *_valueBase; + uint16_t value = (uint16_t(r2) << 8) | uint16_t(r1); + *_valueBase = value; + + if (_hasISR) { + for (int i = 0; i < 16; ++i) { + uint16_t mask = uint16_t(1) << i; + + if (_isrCallback[i] != nullptr && (oldValue & mask) != (value & mask)) { + // log_info("State change pin " << i); + switch (_isrMode[i]) { + case RISING: + if ((value & mask) == mask) { + _isrCallback[i](_isrArgument); + } + break; + case FALLING: + if ((value & mask) == 0) { + _isrCallback[i](_isrArgument); + } + break; + case CHANGE: + _isrCallback[i](_isrArgument); + break; + } + } + } + } + } + + void PCA9539::updatePCAState(void* ptr) { + ISRData* valuePtr = static_cast(ptr); + + BaseType_t xHigherPriorityTaskWoken = false; + xQueueSendFromISR(valuePtr->_container->_isrQueue, &valuePtr, &xHigherPriorityTaskWoken); + } + + void PCA9539::setupPin(pinnum_t index, Pins::PinAttributes attr) { + bool activeLow = attr.has(Pins::PinAttributes::ActiveLow); + bool output = attr.has(Pins::PinAttributes::Output); + + uint64_t mask = uint64_t(1) << index; + _invert = (_invert & ~mask) | (activeLow ? mask : 0); + _configuration = (_configuration & ~mask) | (output ? 0 : mask); + + const uint8_t deviceId = index / 16; + + const uint8_t ConfigReg = 6; + uint8_t address = 0x74 + deviceId; + + uint8_t value = uint8_t(_configuration >> (8 * (index / 8))); + uint8_t reg = ConfigReg + ((index / 8) & 1); + + // log_info("Setup reg " << int(reg) << " with value " << int(value)); + + I2CSetValue(_i2cBus, address, reg, value); + } + + void PCA9539::writePin(pinnum_t index, bool high) { + uint64_t mask = uint64_t(1) << index; + uint64_t oldVal = _value; + uint64_t newVal = high ? mask : uint64_t(0); + _value = (_value & ~mask) | newVal; + + _dirtyRegisters |= ((_value != oldVal) ? 1 : 0) << (index / 8); + } + + bool PCA9539::readPin(pinnum_t index) { + uint8_t reg = uint8_t(index / 8); + uint8_t deviceId = reg / 2; + + // If it's handled by the ISR, we don't need to read anything from the device. + // Otherwise, we do. Check: + if (_isrData[deviceId]._valueBase == nullptr) { + const uint8_t InputReg = 0; + uint8_t address = 0x74 + deviceId; + + auto readReg = InputReg + (reg & 1); + auto value = I2CGetValue(_i2cBus, address, readReg); + uint64_t newValue = uint64_t(value) << (int(reg) * 8); + uint64_t mask = uint64_t(0xff) << (int(reg) * 8); + + _value = ((newValue ^ _invert) & mask) | (_value & ~mask); + + // log_info("Read reg " << int(readReg) << " <- value " << int(newValue) << " gives " << int(_value)); + } + // else { + // log_info("No read, value is " << int(_value)); + // } + + return (_value & (1ull << index)) != 0; + } + + void PCA9539::flushWrites() { + uint64_t write = _value ^ _invert; + for (int i = 0; i < 8; ++i) { + if ((_dirtyRegisters & (1 << i)) != 0) { + const uint8_t OutputReg = 2; + uint8_t address = 0x74 + (i / 2); + + uint8_t val = uint8_t(write >> (8 * i)); + uint8_t reg = OutputReg + (i & 1); + I2CSetValue(_i2cBus, address, reg, val); + } + } + + _dirtyRegisters = 0; + } + + // ISR's: + void PCA9539::attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) { + int device = index / 16; + int pinNumber = index % 16; + + Assert(_isrData[device]._isrCallback[pinNumber] == nullptr, "You can only set a single ISR for pin %d", index); + + _isrData[device]._isrCallback[pinNumber] = callback; + _isrData[device]._isrArgument[pinNumber] = arg; + _isrData[device]._isrMode[pinNumber] = mode; + _isrData[device]._hasISR = true; + } + + void PCA9539::detachInterrupt(pinnum_t index) { + int device = index / 16; + int pinNumber = index % 16; + + _isrData[device]._isrCallback[pinNumber] = nullptr; + _isrData[device]._isrArgument[pinNumber] = nullptr; + _isrData[device]._isrMode[pinNumber] = 0; + + bool hasISR = false; + for (int i = 0; i < 16; ++i) { + hasISR |= (_isrData[device]._isrArgument[i] != nullptr); + } + _isrData[device]._hasISR = hasISR; + } + + const char* PCA9539::name() const { return "pca9539"; } + + PCA9539 ::~PCA9539() { + for (int i = 0; i < 4; ++i) { + auto& data = _isrData[i]; + + if (!data._pin.undefined()) { + data._pin.detachInterrupt(); + } + } + } + + // Register extender: + namespace { + PinExtenderFactory::InstanceBuilder registration("pca9539"); + } +} diff --git a/FluidNC/src/Extenders/PCA9539.h b/FluidNC/src/Extenders/PCA9539.h new file mode 100644 index 000000000..d90068370 --- /dev/null +++ b/FluidNC/src/Extenders/PCA9539.h @@ -0,0 +1,126 @@ +// Copyright (c) 2021 - Stefan de Bruijn +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#pragma once + +#include "PinExtenderDriver.h" +#include "../Configuration/Configurable.h" +#include "../Machine/MachineConfig.h" +#include "../Machine/I2CBus.h" +#include "../Platform.h" + +#include + +namespace Pins { + class PCA9539PinDetail; +} + +namespace Extenders { + // Pin extenders... + // + // The PCA9539 is identical to the PCA9555 in terms of API. It provides 2 address + // pins, so a maximum of 4 possible values. Per PCA, there are 16 I/O ports in 2 + // separate registers, so that's a total of 16*4 = 64 values. + // Datasheet: https://www.ti.com/lit/ds/symlink/pca9539.pdf + // Speed: 400 kHz + // + // The PCA8574 is quite similar as well, but only has 8 bits per device, so a single + // register. It has 3 address pins, so 8 possible values. 8*8=64 bits. + // Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA8574_PCA8574A.pdf + // Speed: 400 kHz + // + // An optional 'interrupt' line can be used. When the 'interrupt' is called, it means + // that *some* pin has changed state. We don't know which one that was obviously. + // However, we can then query the individual pins (thereby resetting them) and throwing + // the results as individual ISR's. + // + // NOTE: The data sheet explains that interrupts can be chained. If that is the case, the + // interrupt will have the effect that ALL PCA's in the chain have to be queried. Needless + // to say, this is usually a bad idea, because things like endstops become much slower + // as a result. For now, I just felt like not supporting it. + // + // The MCP23017 has two interrupt lines, one for register A and register B. Apart from + // that it appears to be quite similar as well. It has 3 address lines and 16 I/O ports, + // so that's a total of 8 * 16 = 128 I/O ports. + // Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf + // Speed: 100 kHz, 400 kHz, 1.7 MHz. + // + // MCP23S17 is similar to MCP23017 but works using SPI instead of I2C (10 MHz). MCP23S08 + // seems to be the same, but 8-bit. + // + // MAX7301 is SPI based, and like all the others, it can generate an ISR when the state + // changes (pin 31). Address is selected like any other SPI device by CS. MAX7301 includes + // pullups and schmitt triggers. + // Datasheet: https://datasheet.lcsc.com/lcsc/1804140032_Maxim-Integrated-MAX7301AAX-_C143583.pdf + class PCA9539 : public PinExtenderDriver { + friend class Pins::PCA9539PinDetail; + + // Address can be set for up to 4 devices. Each device supports 16 pins. + + static const int numberPins = 16 * 4; + uint64_t _claimed; + + Machine::I2CBus* _i2cBus; + + static uint8_t IRAM_ATTR I2CGetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg); + static void IRAM_ATTR I2CSetValue(Machine::I2CBus* bus, uint8_t address, uint8_t reg, uint8_t value); + + // Registers: + // 4x16 = 64 bits. Fits perfectly into an uint64. + uint64_t _configuration = 0; + uint64_t _invert = 0; + volatile uint64_t _value = 0; + + // 4 devices, 2 registers per device. 8 bits is enough: + uint8_t _dirtyRegisters = 0; + + QueueHandle_t _isrQueue = nullptr; + TaskHandle_t _isrHandler = nullptr; + + static void isrTaskLoop(void* arg); + + struct ISRData { + ISRData() = default; + + Pin _pin; + PCA9539* _container = nullptr; + volatile uint16_t* _valueBase = nullptr; + uint8_t _address = 0; + + typedef void (*ISRCallback)(void*); + + bool _hasISR = false; + ISRCallback _isrCallback[16] = { 0 }; + void* _isrArgument[16] = { 0 }; + int _isrMode[16] = { 0 }; + + void IRAM_ATTR updateValueFromDevice(); + }; + + ISRData _isrData[4]; + static void IRAM_ATTR updatePCAState(void* ptr); + + public: + PCA9539() = default; + + void claim(pinnum_t index) override; + void free(pinnum_t index) override; + + void validate() const override; + void group(Configuration::HandlerBase& handler) override; + + void init(); + + void IRAM_ATTR setupPin(pinnum_t index, Pins::PinAttributes attr) override; + void IRAM_ATTR writePin(pinnum_t index, bool high) override; + bool IRAM_ATTR readPin(pinnum_t index) override; + void IRAM_ATTR flushWrites() override; + + void attachInterrupt(pinnum_t index, void (*callback)(void*), void* arg, int mode) override; + void detachInterrupt(pinnum_t index) override; + + const char* name() const override; + + ~PCA9539(); + }; +} From 18d3bd5b90871d90aa2809c89c1e0d70df9ccfdb Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 21 Jul 2022 12:59:18 +0200 Subject: [PATCH 097/100] Fixed a few conflicts. Should all work again, but untested. --- FluidNC/src/ControlPin.h | 5 ++--- FluidNC/src/Machine/I2CBus.cpp | 25 +++---------------------- FluidNC/src/Machine/LimitPin.h | 2 +- FluidNC/src/Machine/Motor.cpp | 4 ---- 4 files changed, 6 insertions(+), 30 deletions(-) diff --git a/FluidNC/src/ControlPin.h b/FluidNC/src/ControlPin.h index 459c0465e..4a98e5ece 100644 --- a/FluidNC/src/ControlPin.h +++ b/FluidNC/src/ControlPin.h @@ -9,9 +9,6 @@ class ControlPin { volatile bool& _rtVariable; // The variable that is set when the pin is asserted int32_t _debounceEnd = 0; - void IRAM_ATTR handleISR(); - CreateISRHandlerFor(ControlPin, handleISR); - // Interval during which we ignore repeated control pin activations const int debounceUs = 10000; // 10000 us = 10 ms void IRAM_ATTR handleISR(); @@ -31,5 +28,7 @@ class ControlPin { void init(); bool get() { return _value; } + String report(); + ~ControlPin(); }; diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index e62b9d1de..60764ea40 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace Machine { void I2CBus::validate() const { @@ -39,29 +40,9 @@ namespace Machine { } const char* I2CBus::ErrorDescription(int code) { - switch (code) { - case I2C_ERROR_OK: - return "ok"; - case I2C_ERROR_DEV: - return "general device error"; - case I2C_ERROR_ACK: - return "no ack returned by device"; - case I2C_ERROR_TIMEOUT: - return "timeout"; - case I2C_ERROR_BUS: - return "bus error"; - case I2C_ERROR_BUSY: - return "device busy"; - case I2C_ERROR_MEMORY: - return "insufficient memory"; - case I2C_ERROR_CONTINUE: - return "continue"; - case I2C_ERROR_NO_BEGIN: - return "begin transmission missing"; - default: - return "unknown"; - } + return esp_err_to_name(code); } + int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { // log_debug("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " // << (i2c ? "non null" : "null")); diff --git a/FluidNC/src/Machine/LimitPin.h b/FluidNC/src/Machine/LimitPin.h index 509907c25..8a6a60647 100644 --- a/FluidNC/src/Machine/LimitPin.h +++ b/FluidNC/src/Machine/LimitPin.h @@ -35,7 +35,7 @@ namespace Machine { void read(); public: - LimitPin(Pin& pin, int axis, int motorNum, int direction, bool& phardLimits, bool& pLimited); + LimitPin(Pin& pin, int axis, int motor, int direction, bool& pHardLimits, bool& pLimited); Pin& _pin; diff --git a/FluidNC/src/Machine/Motor.cpp b/FluidNC/src/Machine/Motor.cpp index c965a0e42..39f20e7c7 100644 --- a/FluidNC/src/Machine/Motor.cpp +++ b/FluidNC/src/Machine/Motor.cpp @@ -30,10 +30,6 @@ namespace Machine { void Motor::init() { log_debug("Initializing motor / limits..."); - _negLimitPin = new LimitPin(_negPin, _axis, _motorNum, -1, _hardLimits); - _posLimitPin = new LimitPin(_posPin, _axis, _motorNum, 1, _hardLimits); - _allLimitPin = new LimitPin(_allPin, _axis, _motorNum, 0, _hardLimits); - if (strcmp(_driver->name(), "null_motor") != 0) { set_bitnum(Machine::Axes::motorMask, Machine::Axes::motor_bit(_axis, _motorNum)); } From 2f1ac3c3bf618886b89d6bd6862822759e95d9b3 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Thu, 21 Jul 2022 13:06:48 +0200 Subject: [PATCH 098/100] Fixed error description. --- FluidNC/src/Machine/I2CBus.cpp | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index e62b9d1de..41c4f0d51 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -38,30 +38,8 @@ namespace Machine { log_info("I2C SDA: " << _sda.name() << ", SCL: " << _scl.name() << ", Freq: " << _frequency << ", Bus #: " << _busNumber); } - const char* I2CBus::ErrorDescription(int code) { - switch (code) { - case I2C_ERROR_OK: - return "ok"; - case I2C_ERROR_DEV: - return "general device error"; - case I2C_ERROR_ACK: - return "no ack returned by device"; - case I2C_ERROR_TIMEOUT: - return "timeout"; - case I2C_ERROR_BUS: - return "bus error"; - case I2C_ERROR_BUSY: - return "device busy"; - case I2C_ERROR_MEMORY: - return "insufficient memory"; - case I2C_ERROR_CONTINUE: - return "continue"; - case I2C_ERROR_NO_BEGIN: - return "begin transmission missing"; - default: - return "unknown"; - } - } + const char* I2CBus::ErrorDescription(int code) { return esp_err_to_name(code); } + int I2CBus::write(uint8_t address, const uint8_t* data, size_t count) { // log_debug("I2C write addr=" << int(address) << ", count=" << int(count) << ", data " << (data ? "non null" : "null") << ", i2c " // << (i2c ? "non null" : "null")); From fb56fffeb72536b28381358485990869ccd32e4c Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 22 Jul 2022 13:25:35 +0200 Subject: [PATCH 099/100] Added motor position $MP / $Motors/Position command. --- FluidNC/src/Machine/Motor.h | 7 ++++--- FluidNC/src/ProcessSettings.cpp | 18 ++++++++++++++++++ FluidNC/src/System.cpp | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/FluidNC/src/Machine/Motor.h b/FluidNC/src/Machine/Motor.h index fd78401cf..cec791ee7 100644 --- a/FluidNC/src/Machine/Motor.h +++ b/FluidNC/src/Machine/Motor.h @@ -35,9 +35,10 @@ namespace Machine { Pin _allPin; bool _hardLimits = false; - int32_t _steps = 0; - bool _limited = false; // _limited is set by the LimitPin ISR - bool _blocked = false; // _blocked is used during asymmetric homing pulloff + int32_t _stepsOffset = 0; + int32_t _steps = 0; + bool _limited = false; // _limited is set by the LimitPin ISR + bool _blocked = false; // _blocked is used during asymmetric homing pulloff // Configuration system helpers: void group(Configuration::HandlerBase& handler) override; diff --git a/FluidNC/src/ProcessSettings.cpp b/FluidNC/src/ProcessSettings.cpp index bb203d8de..25da260f8 100644 --- a/FluidNC/src/ProcessSettings.cpp +++ b/FluidNC/src/ProcessSettings.cpp @@ -596,6 +596,23 @@ static Error motors_init(const char* value, WebUI::AuthenticationLevel auth_leve return Error::Ok; } +static Error motorPosition(const char* value, WebUI::AuthenticationLevel auth_level, Channel& out) { + out << "[MSG: DBG: Motor position: ("; + auto axes = config->_axes; + auto n_axis = axes->_numberAxis; + for (size_t axis = 0; axis < n_axis; axis++) { + auto m = axes->_axis[axis]->_motors[0]; + auto steps = m ? (m->_steps + m->_stepsOffset) : 0; + if (axis != 0) { + out << ", "; + } + out << steps; + } + out << ")]\n"; + + return Error::Ok; +} + static Error macros_run(const char* value, WebUI::AuthenticationLevel auth_level, Channel& out) { if (value) { log_info("Running macro " << *value); @@ -723,6 +740,7 @@ void make_user_commands() { new UserCommand("H", "Home", home_all, notIdleOrAlarm); new UserCommand("MD", "Motor/Disable", motor_disable, notIdleOrAlarm); new UserCommand("MI", "Motors/Init", motors_init, notIdleOrAlarm); + new UserCommand("MP", "Motors/Position", motorPosition, anyState); new UserCommand("RM", "Macros/Run", macros_run, notIdleOrAlarm); diff --git a/FluidNC/src/System.cpp b/FluidNC/src/System.cpp index a610dcc99..659042de1 100644 --- a/FluidNC/src/System.cpp +++ b/FluidNC/src/System.cpp @@ -53,6 +53,7 @@ void set_motor_steps(size_t axis, int32_t steps) { for (size_t motor = 0; motor < Machine::Axis::MAX_MOTORS_PER_AXIS; motor++) { auto m = a->_motors[motor]; if (m) { + m->_stepsOffset += (m->_steps - steps); m->_steps = steps; } } From 3b927169487c24875de9a6472a1280215f654559 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Sat, 23 Jul 2022 13:42:27 +0200 Subject: [PATCH 100/100] Added acceleration that is proportional to the requested feed rate. This should ensure that you can set high accelerations for rapids without compromising high torque jobs that require a low feed rate. --- FluidNC/src/Machine/Axis.cpp | 2 ++ FluidNC/src/Machine/Axis.h | 12 +++++++----- FluidNC/src/NutsBolts.cpp | 12 ++++++++++-- FluidNC/src/NutsBolts.h | 2 +- FluidNC/src/Planner.cpp | 17 +++++++++++++---- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/FluidNC/src/Machine/Axis.cpp b/FluidNC/src/Machine/Axis.cpp index a1c9993e3..483157781 100644 --- a/FluidNC/src/Machine/Axis.cpp +++ b/FluidNC/src/Machine/Axis.cpp @@ -9,6 +9,8 @@ namespace Machine { handler.item("steps_per_mm", _stepsPerMm, 0.001, 100000.0); handler.item("max_rate_mm_per_min", _maxRate, 0.001, 100000.0); handler.item("acceleration_mm_per_sec2", _acceleration, 0.001, 100000.0); + handler.item("min_acceleration_mm_per_sec2", _minAcceleration, 0, 100000.0); + handler.item("proportional_acceleration", _proportionalAcceleration); handler.item("max_travel_mm", _maxTravel, 0.1, 10000000.0); handler.item("soft_limits", _softLimits); handler.section("homing", _homing); diff --git a/FluidNC/src/Machine/Axis.h b/FluidNC/src/Machine/Axis.h index 13d0e9e4d..b6f179875 100644 --- a/FluidNC/src/Machine/Axis.h +++ b/FluidNC/src/Machine/Axis.h @@ -30,11 +30,13 @@ namespace Machine { Motor* _motors[MAX_MOTORS_PER_AXIS]; Homing* _homing = nullptr; - float _stepsPerMm = 80.0f; - float _maxRate = 1000.0f; - float _acceleration = 25.0f; - float _maxTravel = 1000.0f; - bool _softLimits = false; + float _stepsPerMm = 80.0f; + float _maxRate = 1000.0f; + float _acceleration = 25.0f; + float _minAcceleration = 5.0f; + float _maxTravel = 1000.0f; + bool _softLimits = false; + bool _proportionalAcceleration = false; // Configuration system helpers: void group(Configuration::HandlerBase& handler) override; diff --git a/FluidNC/src/NutsBolts.cpp b/FluidNC/src/NutsBolts.cpp index 6d529ee26..7590c7501 100644 --- a/FluidNC/src/NutsBolts.cpp +++ b/FluidNC/src/NutsBolts.cpp @@ -161,13 +161,21 @@ float convert_delta_vector_to_unit_vector(float* v) { const float secPerMinSq = 60.0 * 60.0; // Seconds Per Minute Squared, for acceleration conversion -float limit_acceleration_by_axis_maximum(float* unit_vec) { +float limit_acceleration_by_axis_maximum(float* unit_vec, float factor) { float limit_value = SOME_LARGE_VALUE; auto n_axis = config->_axes->_numberAxis; for (size_t idx = 0; idx < n_axis; idx++) { auto axisSetting = config->_axes->_axis[idx]; if (unit_vec[idx] != 0) { // Avoid divide by zero. - limit_value = MIN(limit_value, fabsf(axisSetting->_acceleration / unit_vec[idx])); + float maxAccel = axisSetting->_acceleration; + if (axisSetting->_proportionalAcceleration) { + maxAccel *= factor; + if (maxAccel < axisSetting->_minAcceleration) { + maxAccel = axisSetting->_minAcceleration; + } + } + + limit_value = MIN(limit_value, fabsf(maxAccel / unit_vec[idx])); } } // The acceleration setting is stored and displayed in units of mm/sec^2, diff --git a/FluidNC/src/NutsBolts.h b/FluidNC/src/NutsBolts.h index c405efdc1..4ecad2a90 100644 --- a/FluidNC/src/NutsBolts.h +++ b/FluidNC/src/NutsBolts.h @@ -94,7 +94,7 @@ float vector_length(float* v, size_t n); void scale_vector(float* v, float scale, size_t n); float convert_delta_vector_to_unit_vector(float* vector); -float limit_acceleration_by_axis_maximum(float* unit_vec); +float limit_acceleration_by_axis_maximum(float* unit_vec, float factor); float limit_rate_by_axis_maximum(float* unit_vec); bool char_is_numeric(char value); diff --git a/FluidNC/src/Planner.cpp b/FluidNC/src/Planner.cpp index fededfafe..9f77569d2 100644 --- a/FluidNC/src/Planner.cpp +++ b/FluidNC/src/Planner.cpp @@ -333,10 +333,10 @@ bool plan_buffer_line(float* target, plan_line_data_t* pl_data) { // down such that no individual axes maximum values are exceeded with respect to the line direction. // NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes, // if they are also orthogonal/independent. Operates on the absolute value of the unit vector. - block->millimeters = convert_delta_vector_to_unit_vector(unit_vec); - block->acceleration = limit_acceleration_by_axis_maximum(unit_vec); - block->rapid_rate = limit_rate_by_axis_maximum(unit_vec); + block->millimeters = convert_delta_vector_to_unit_vector(unit_vec); + // Store programmed rate. + float accelerationFactor = 1; if (block->motion.rapidMotion) { block->programmed_rate = block->rapid_rate; } else { @@ -344,7 +344,16 @@ bool plan_buffer_line(float* target, plan_line_data_t* pl_data) { if (block->motion.inverseTime) { block->programmed_rate *= block->millimeters; } + + accelerationFactor = block->programmed_rate / block->rapid_rate; + if (accelerationFactor > 1) { + accelerationFactor = 1; + } } + + block->acceleration = limit_acceleration_by_axis_maximum(unit_vec, accelerationFactor); + block->rapid_rate = limit_rate_by_axis_maximum(unit_vec); + // TODO: Need to check this method handling zero junction speeds when starting from rest. if ((block_buffer_head == block_buffer_tail) || (block->motion.systemMotion)) { // Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later. @@ -389,7 +398,7 @@ bool plan_buffer_line(float* target, plan_line_data_t* pl_data) { block->max_junction_speed_sqr = SOME_LARGE_VALUE; } else { convert_delta_vector_to_unit_vector(junction_unit_vec); - float junction_acceleration = limit_acceleration_by_axis_maximum(junction_unit_vec); + float junction_acceleration = limit_acceleration_by_axis_maximum(junction_unit_vec, accelerationFactor); float sin_theta_d2 = sqrtf(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive. block->max_junction_speed_sqr = MAX(MINIMUM_JUNCTION_SPEED * MINIMUM_JUNCTION_SPEED,