Category: Uncategorized

Self balancing robot improvements

I was dissatisfied with robots performance, so I experimented a lot with various aspects to improve it. But along the way the robot, however simple in concept, proved to be pretty complex system which depends on many factors that I didn’t anticipated. This variety of factors which altogether influence its movements make it very difficult to troubleshoot problems. So, now I’ll try to recall my adventures with it in order. Oh, and my objective (best case scenario) is to make it stable to the point it stands completely still. This is what I found the most difficult.

Better motors

Long (tall) version with hard tires.

I decided, my first set of motors (Dagu DG02S 48:1) have to low torque on low RPM (driven from DRV8835 H-Bridge again with PWM). The robot was able to stand, but wiggled (video in previous post). So I changed them to JK28HS32 stepper motors, which proved to be even weaker. They simply have to low torque in all RPM range for the weight of my robot. Another disadvantages:

  • Draws tons of current even when robot is stationary. This is because un-geared steppers (at least small ones) spin freely when unpowered and thus need current which hold them in position.
  • More complex electronics (1 H-bridge per motor instead of 1/2 per brushed one), and much more complex programming.
  • Intense vibration even in half stepping mode. And I used 200step ones. The solution for that would be to use smaller wheels with soft tires or implement micro stepping which would in turn reduce its incremental torque. Every time I use one of these steppers, and I dig into the subject I realize how difficult a stupid motor might get.

So, not without disappointment I moved to another set of motors which happened to be Brushless DC Motor with Encoder 12V 159RPM from DFrobot (I found them on Aliexpress BTW). To my consternation it didn’t help much. Advantages of the newly mounted motors are its high torque, ease of interfacing (they have controller stuffed inside), but cons are:

  • They have quite some backlash.
  • They run on 12V, so I had to rework some of the electronics.
  • They might be a little bit faster, though at the end I was able to stabilize the thing pretty satisfactory.

Geared BLDC motors

To sum up : high quality motors eliminate one point of failure and lets you focus on another aspects if something is failing. My ideal motors, that I would like to have are:

  • Precise in terms of control. Wide range of RPMs i.e. able to spin super slow or quite fast with decent torque in all situations. What is a decant torque? Dunno. 2,4 kg*cm?
  • Fast. I would love to have 300RPM.
  • Minimal backlash.
  • Encoders built in.
  • Not to mention power efficiency and ease of programming, but this is not the most important thing.

Surface

So then I finally decided, that I stop there with changing motors, and wont replace them for the 4rd time, but try to tune the PID and fiddle with the software. I replaced my fusion algorithm from simple complementary one to Madgwick, which open source implementation is available online, and it is a part of a PhD thesis of some clever guy (Sebastian Madgwick). I cannot stress how far superior this algorithm is over my humble thing. But no luck with that neither. If I increased Kp and Ki it would oscillate vigorously (Kd was of not  much help there), and when I decreased Kp and especially Ki, the robot would always drift away gaining speed and tipping over. Playing with the dreaded thing which would not stand at all, and wiggle as some drunk I recalled, that on many YT videos (user upgrdman among my favorites on the subject) people was driving their robots on soft surfaces like carpets and that made me wonder. So I put my robot on a blanket and it helped a little. It reduced both oscillations and the drifting. I have theory, that firm surface (and/or soft tires) first damps vibrations, but also when type flattens under the weight, it somewhat blocks further movements. Hard tires do not have this effect and on hard surfaces they spin whenever robot is only slightly out of its balance whether on soft ones this slight error can be counteracted by deformed wheel to some very small extent. Think of dry sand on the beach and big soft wheels, I think even when unpowered, the robot couldn’t have a chance to stand still. So it thought about changing the wheels (and tires), because at that time I used pretty hard wheels from hardware store (some furniture ones) and I ordered 70mm and 110mm squishy wheels for model aircrafts. And then, while waiting for the wheels I made hasty decision to shorten the thing.

Length

Bear in mind, that I can be completely wrong here! As far as I understand, when you have normal pendulum, the longer the string is, the faster will be linear velocity of the pendulum bob. I assumed The same thing will be with inverted pendulum, thus the taller the robot, the bigger velocity of it top-end when tipping over. Thus, I concluded, the faster lower-end movements will be necessary to counteract the moving top. At that time I thought my motors are to slow, so It seemed to be a good idea to shorten the body. So I did. And improvement was negligible if at all. Also, from the pendulum frequency equation I knew, that shortening the length increases the pendulum frequency, so i understood that my algorithm must react faster from now on.

Mechanical issues

Then I realized, that the wheels got loose. I ordered bunch of adapters and fixed them in place. Still no luck.

Scratching head and tuning PID

I couldn’t sleep but visualized my dangling robot. I tried to tune the PID controller a lot. And still every time I increased Ki it oscillated, and when decreased it drifted away. Too much Ki and it oscillated left and right, and too much Kd and it oscillated but in like one direction. Like hiccup. Derivative term damps output when error decreases, so in theory the robot should decelerate near sweet point and make full stop, but instead it would stop for a moment (like millisecond moment), and start in the same direction, then stop and then again, all in sudden sequence.

nRF24L01+ distraction

I was frustrated and sick and tired, so decided to do something different, as a break from the main subject. I got into nRF24 code and telemetry. I wanted to make full telemetry for the project as user upgrdman did. I thought that it would help me to debug. Then I run into another problems, procrastinated a little bit more with refactoring my SPI library and so on. I had curious adventures with nRF though, but this is completely other story. I use excellent application called KST2 for plotting the data. From the plots I observed that robot is drifting due to sudden integral peaks. It’s like robot was balancing for a moment, and then the pitch plot would show slight shift towards one direction, and just after that the integral part grew, robot started to move and tried to catch up, the i increased and increased, robot speed up, and then output saturated and thats all. The only thing which helped a little was to increase Ki (it amplified the reaction for integral, so robot drove faster, so was able to catch up and straighten), but it also increased oscillations.

Main loop frequency

In the act of desperation, not hoping for a change for better, I increased main loop frequency from 100Hz to 1kHz. I could do that, because I use very fast STM32F4 (compared to Arduinos and Atmegas that everybody seems to love so much) with a fpu. And that was it. It calmed the robot so much, that I could increase Ki to the values not possible before. Then I mounted the 110mm squishy wheels and it helped even more for the reasons described above, and because bigger wheels gives more speed. Thats all for now, I’m a little bit tired of this project, but in future I plan to:

  • Program encoders.
  • Program remote control (I bought myself nice Devo 7E transmitter).
  • Experiment with all (or most) of the parameters I talked about. Height vs. loop frequency, various wheels and so on.

Wheels galore

Self balancing robot first tests

I have built a self balancing robot, and here I want to post some notes regarding problems that I encountered. It was (as usual) more difficult than I anticipated, but I guess every hacker know this feeling.

Frame

Frame is made of 8mm aluminium pipes, 2 sheets of 2mm solid transparent polycarbonate, and aluminium angle bars. The pieces are screwed together with threaded rods and self locking nuts.  Making the frame was the easiest part, so I won’t write much about it.

Motors

Motors are labeled “Dagu DG02S 48:1”, and I am disappointed, because they have very low torque at low RPM, and are of low quality I think (their low price also suggests that). The consequences are, that they are unable to move my robot gently and precisely, but instead they start all of a sudden with couple of RPMs. At very low PWM duty cycle they are unable to move wheels at all, even with no load. The lowest RPM i can get out of them is probably a couple of RPMs, which sounds OK, but I think, that many of my problems arise from them being unable to gently move the wheels few millimeters forth and back. Such gentle movement is in my opinion crucial for steady (nearly stationary) operation, that you have on certain YouTube videos. So next tests (if there will be any) will be performed with steppers or geared brushless motors. Oh, and there are no encoders neither.

Electronics

If motors are the brawl, electronics are the brain. Main board is STM32F4-DISCO and is connected to the battery via custom per-boards with connectors. On the top of the robot there is a single 16850 Samsung cell paired with cheap LiPo charger, and switch. I chose it, because it is a lot cheaper per mAh than those silverish ones. Battery sits inside bracket ordered on AliExpress. As of accelerometer and gyroscope, the super popular MPU 6050 does the job (also AliExpress). Motors are driven by Pololu DRV8835, and last but not least nRF24L01+ is used for connectivity with the robot, which is crucial to tune PID controller without dangling cables which would degrade stability, and make center of mass unstable. Like shooting to a moving target.

Oh, and I wanted to control the robot using cheap CX-10 remote, but It failed completely. I based my code on deviation and others work, but after many hours I gave up. Then I’ve got Syma X5HW for Christmas, and with its remote I finally had success (after first try, like 10 minutes of coding, not everyday something like this happens. But of course I only had to modify parameters for my CX-10 code). At first I confused the binding channel number (I set 0 instead of 8), and I was still able to receive data, but only from approx 5 cm apart. Then after setting it correctly to channel 8 range increased dramatically.

Programming

With electronics and motors on its place, there comes programming, the most difficult part. First I made peripherals of the robot to work (Arduino library called i2cdevlib was very helpful), so I was able to read raw data from MPU 6050, send basic commands via nRF, and spin the motors. Then, the most challenging part was to implement:

  • pitch angle calculation (http://www.starlino.com/imu_guide.html),
  • fusion of gyroscope and accelerometer data (this was helpful : http://www.pieter-jan.com/node/11),
  • PID tuning (though implementing a PID is dead simple, tuning it is completely other story). Most helpful for me in this regard was probably the Wikipedia article on PID controller. In my case, the most (positive) impact came from integral part, where D part seems to have minimal influence.

I’ll try to force myself to get into detail on the topics above, as I only scratched the surface.

Implementing zoom with moving pivot point using libclutter

I’m making this post, because I struggled with this functionality a lot! I was implementing it (to the extent I was happy with) in the course of 4 or even 5 days! Ok, first goes an animated gif which shows my desired zoom behavior (check out Inkscape, or other graphics programs, they all work the same in this regard):

Desired zoom functionality

Basically the idea is that, the center of the scale transformation is where the mouse pointer is on the screen, so the object under the cursor does not move while scaling, whereas other objects around it do. My first, and most obvious implementation (which didn’t work as expected) was as such:

/*
 * Point center is in screen coordinates. This is where mouse pointer is on the screen.
 * The layout is : GtkWindow has ClutterStage which contains ClutterActor scaleLayer
 * (in this function named self), which contains those blue
 * circles you see on animated gif.
 *
 * ScaleLayer is simply a huge invisible plane which contains all the objects an user
 * interacts with. User can pan and zoom it as he whishes.
 */
void ScaleLayer::zoomOut (const Point &center)
{
        double scaleX, scaleY;
        // This gets our actual zoom factor.
        clutter_actor_get_scale (self, &scaleX, &scaleY);
        // We decrease the zoom factor since this is a zoomOut method.
        float newScale = scaleX / 1.1;

        float cx1, cy1;
        // Like I said 'center' is in screen coords, so we convert it to scaleLayer coords
        clutter_actor_transform_stage_point (self, center.x, center.y, &cx1, &cy1);

        float scaleLayerNW, scaleLayerNH;
        clutter_actor_get_size (self, &scaleLayerNW, &scaleLayerNH);
        // We set pivot_point
        clutter_actor_set_pivot_point (self, double(cx1) / scaleLayerNW, double(cy1) / scaleLayerNH);
        // And finalyy perform the scalling. Fair enough, isn't it?
        clutter_actor_set_scale (self, newScale, newScale);
}

Here is the outcome of the above:

Zoom fail

It is fine until you move the mouse cursor which changes the pivot point (center of the scale transformation) while scale is not == 1.0. I dunno why this happens. Apparently I do not understand affine transformations as well as I thought, or there is a bug in libclutter (I doubt it). The solution is to convert the pivot point from screen to scaleLayer coordinates before scaling (as I did), and again after scaling. The difference is in scaleLayer coordinates, so it must be converted back to screen coordinates, and the result can be used to cancel this offset you see on the second gif. Here is my current implementation:

void ScaleLayer::zoomIn (const Point &center)
{
        double x, y;
        clutter_actor_get_scale (self, &x, &y);

        if (x >= 10) {
                return;
        }

        double newScale = x * 1.1;

        if (newScale >= 10) {
                newScale = 10;
        }

        scale (center, newScale);
}

/*****************************************************************************/

void ScaleLayer::zoomOut (const Point &center)
{
        ClutterActor *stage = clutter_actor_get_parent (self);

        float stageW, stageH;
        clutter_actor_get_size (stage, &stageW, &stageH);

        float dim = std::max (stageW, stageH);
        double minScale = dim / SCALE_SURFACE_SIZE + 0.05;

        double scaleX, scaleY;
        clutter_actor_get_scale (self, &scaleX, &scaleY);

        if (scaleX <= minScale) { return; } scale (center, scaleX / 1.1); 
} 

/*****************************************************************************/ 

void ScaleLayer::scale (Point const &c, float newScale) { 
   Point center = c; float cx1, cy1; if (center == Point ()) { if (impl->lastCenter == Point ()) {
                        float stageW, stageH;
                        ClutterActor *stage = clutter_actor_get_parent (self);
                        clutter_actor_get_size (stage, &stageW, &stageH);
                        impl->lastCenter = Point (stageW / 2.0, stageH / 2.0);
                }

                center = impl->lastCenter;
        }
        else {
                impl->lastCenter = center;
        }

        clutter_actor_transform_stage_point (self, center.x, center.y, &cx1, &cy1);
        float scaleLayerNW, scaleLayerNH;
        clutter_actor_get_size (self, &scaleLayerNW, &scaleLayerNH);
        clutter_actor_set_pivot_point (self, double(cx1) / scaleLayerNW, double(cy1) / scaleLayerNH);
        clutter_actor_set_scale (self, newScale, newScale);

        // Idea taken from here : https://community.oracle.com/thread/1263955
        float cx2, cy2;
        clutter_actor_transform_stage_point (self, center.x, center.y, &cx2, &cy2);

        ClutterVertex vi1 = { 0, 0, 0 };
        ClutterVertex vo1;
        clutter_actor_apply_transform_to_point (self, &vi1, &vo1);

        ClutterVertex vi2 = { cx2 - cx1, cy2 - cy1, 0 };
        ClutterVertex vo2;
        clutter_actor_apply_transform_to_point (self, &vi2, &vo2);

        float mx = vo2.x - vo1.x;
        float my = vo2.y - vo1.y;

        clutter_actor_move_by (self, mx, my);
}

The whole project is here : https://github.com/iwasz/data-flow-gui
Here’s the thread which pointed me in right direction : https://community.oracle.com/thread/1263955

Cross compilation with GCC & QtCreator for ARM Cortex M

This post is outdated!!. Please see http://www.iwasz.pl/electronics/stm32-on-ubuntu-linux-step-by-step/ for more up to date instructions.

I used Eclipse CDT for years for C/C++ and was disappointed by its bulkiness, slowness and memory usage. I did mostly embedded, and sometimes GTK+ desktop apps (even OpenGL once or twice). I looked for a replacement, tried dozen or more IDEs and editors, and finally found out the QtCreator (my main concerns were : great code navigation – eclipse often get confused wit serious, templated C++ code), great code completion, and CMake integration. I am satisfied for now (It’s been a year now), and I use it for all but Qt. But all of a sudden, wen a new version appeared, I came across a minor flaw in CMake builder, which reported an error like “Can’t link a test program”. Obviously he cannot, because he used host compiler instead of ARM one.

So in version prior to 4.0.0 i used to configure my project with cmake like:

cd build
cmake ..
make

Then, from Qt, i simply compiled the project, and it worked flawlessly. But since QtCreator 4.0.0 it started to invoke cmake in every possible situation. Be it a IDE startup, or saving a CMakeLists.txt file. And he did it with -DCMAKE_CXX_COMPILER=xyz where “xyz” was a path configured in Tools -> Options -> Build & Run -> Compilers. If I run cmake manually, without this CMAKE_CXX_COMPILER variable set, everything was OK. I saw in the Internet, that many people had the same problem, and used QtCreator for embedded like I do (see comments here).

So I decided, that instead of forcing QtCreator to stop invoking cmake, or invoking it with different parameters, I should fix my CMakeLists.txt so it would run as QtCreator want it. Solution I found was CMAKE_FORCE_C_COMPILER and CMAKE_FORCE_CXX_COMPILER documented here. My CMakeLists.txt looks like this:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

SET (CMAKE_VERBOSE_MAKEFILE OFF)
include (stm32.cmake)

PROJECT (robot1)

SET(USB_LIB "" CACHE STRING "")

INCLUDE_DIRECTORIES("src/")
LIST (APPEND APP_SOURCES "src/main.c")
LIST (APPEND APP_SOURCES "src/stm32f4xx_it.c")
LIST (APPEND APP_SOURCES "src/syscalls.c")
LIST (APPEND APP_SOURCES "src/system_stm32f0xx.c")
LIST (APPEND APP_SOURCES "src/config.h")
LIST (APPEND APP_SOURCES "src/stm32f0xx_hal_conf.h")

AUX_SOURCE_DIRECTORY ("${CUBE_ROOT}/Drivers/STM32F0xx_HAL_Driver/Src/" APP_SOURCES)
LIST (APPEND APP_SOURCES "${STARTUP_CODE}")

ADD_EXECUTABLE(${CMAKE_PROJECT_NAME}.elf ${APP_SOURCES})
ADD_CUSTOM_TARGET(${CMAKE_PROJECT_NAME}.bin ALL DEPENDS ${CMAKE_PROJECT_NAME}.elf COMMAND ${CMAKE_OBJCOPY} -Obinary ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.bin)

FIND_PROGRAM (ST_FLASH st-flash)
ADD_CUSTOM_TARGET("upload" DEPENDS ${CMAKE_PROJECT_NAME}.elf COMMAND ${ST_FLASH} --reset write ${CMAKE_PROJECT_NAME}.bin 0x8000000)

And the “toolchain file” is like this:

SET (TOOLCHAIN_PREFIX "/home/iwasz/local/share/armcortexm0-unknown-eabi" CACHE STRING "")
SET (TARGET_TRIPLET "armcortexm0-unknown-eabi" CACHE STRING "")
SET (DEVICE "STM32F072xB")
SET (CUBE_ROOT "/home/iwasz/workspace/stm32cubef0")
SET (CRYSTAL_HZ 16000000)
SET (STARTUP_CODE "src/startup_stm32f072xb.s")
SET (LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/src/STM32F072RB_FLASH.ld")
SET (TOOLCHAIN_BIN_DIR ${TOOLCHAIN_PREFIX}/bin)
SET (TOOLCHAIN_INC_DIR ${TOOLCHAIN_PREFIX}/${TARGET_TRIPLET}/include)
SET (TOOLCHAIN_LIB_DIR ${TOOLCHAIN_PREFIX}/${TARGET_TRIPLET}/lib)

INCLUDE(CMakeForceCompiler)
SET (CMAKE_SYSTEM_NAME Generic)
SET (CMAKE_SYSTEM_PROCESSOR arm)

CMAKE_FORCE_C_COMPILER ("${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-gcc" GNU)
CMAKE_FORCE_CXX_COMPILER ("${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-gcc" GNU)
SET (CMAKE_ASM_COMPILER "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-as")
SET (CMAKE_ASM-ATT_COMPILER "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-as")
SET (CMAKE_OBJCOPY ${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-objcopy)
SET (CMAKE_OBJDUMP ${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-objdump)
SET (CMAKE_C_FLAGS "-std=gnu99 -fdata-sections -ffunction-sections -Wall" CACHE INTERNAL "c compiler flags")
SET (CMAKE_CXX_FLAGS "-std=c++11 -Wall -fdata-sections -ffunction-sections -MD -Wall" CACHE INTERNAL "cxx compiler flags")
SET (CMAKE_EXE_LINKER_FLAGS "-T ${LINKER_SCRIPT} -Wl,--gc-sections" CACHE INTERNAL "exe link flags")
SET (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
SET (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")

INCLUDE_DIRECTORIES(${SUPPORT_FILES})
LINK_DIRECTORIES(${SUPPORT_FILES})
ADD_DEFINITIONS(-D${DEVICE})
INCLUDE_DIRECTORIES("${CUBE_ROOT}/Drivers/STM32F0xx_HAL_Driver/Inc/")
INCLUDE_DIRECTORIES("${CUBE_ROOT}/Drivers/CMSIS/Device/ST/STM32F0xx/Include/")
INCLUDE_DIRECTORIES("${CUBE_ROOT}/Drivers/CMSIS/Include/")

ENABLE_LANGUAGE (ASM-ATT)

Two most important changes were:

  • Using CMAKE_FORCE_CXX_COMPILER macro instead of simply setting CMAKE_CXX_COMPILER var.
  • Including the toolchain file (and thus setting / forcing the compiler) before PROJECT macro.

My configuration as of writing this:

  • Qt Creator 4.0.2, Based on Qt 5.7.0 (GCC 4.9.1 20140922 (Red Hat 4.9.1-10), 64 bit), Built on Jun 13 2016 01:05:36, From revision 47b4f2c738
  • Host system : Ubuntu 15.10, Linux ingram 4.2.0-38-generic #45-Ubuntu SMP Wed Jun 8 21:21:49 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
  • Host GCC : gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
  • Target GCC : armcortexm0-unknown-eabi-gcc (crosstool-NG crosstool-ng-1.21.0-74-g6ac93ed – iwasz) 5.2.0

BlueNRG + STM32F7-DISCO tests

IMG_20160321_020856

  • Tested 32bit cross-compiler from launchpad, it worked OK, but binaries were much bigger, and debugger lacked Python support which is required by QtCreator which is an IDE of my choice (for all C/C++ development including embedded).
  • Made my own compiler with crosstool-NG (master from GIT). Used my own tutorial, which can be found somewhere over this blog.
  • Verified that a “hello world” / “blinky” program works. It did not, because I copied code for STM32F4, and I messed the clock configuration. Examples from STM32F7-cube helped.
  • I ported my project which aimed to bring my BlueNRG dev board to life. The project consists of a USB vendor-specific class for debugging (somewhat more advanced, and tuned for the purpose than simple CDC class), slightly adapted BlueNRG example code from ST (for Nucleo), and some glue code.
  • I switched to STM32F7-disco because I couldn’t get BlueNRG to work with STM32F4-disco. F7 has Arduino connector on the back, so I thought it’d be better than bunch of loosely connected wires which was required for F4. At that time I thought that there is something wrong with my wiring.
  • On STM32F7-disco BLE still would not work. I hooked up a logic analyzer, and noticed that MISO and CLK is silent. It turned up that I have to use alternative CLK configuration on BlueNRG board which required to move 0R resistor from R10 to R11. Although it was small (0402), at first I had problem desoldering it, probably because of unleaded solder used. See image.

IMG_20160320_175826

  • Next problem I had, was with SPI_CS pin. According to the BlueNRG-dev board docs, I wanted to use D8 pin (this is PI2 pin on STM32F7-disco), but it didn’t work. Glimpse on the schematics revealed that the SPI_CS is connected to A1, which is marked as “alternative” in the docs. So IMHO this is an error in the docs.
  • Final pin configuration that worked was:
// SPI Reset Pin
#define BNRG_SPI_RESET_PIN GPIO_PIN_3
#define BNRG_SPI_RESET_PORT GPIOI
// SCLK
#define BNRG_SPI_SCLK_PIN GPIO_PIN_1
#define BNRG_SPI_SCLK_PORT GPIOI
// MISO (Master Input Slave Output)
#define BNRG_SPI_MISO_PIN GPIO_PIN_14
#define BNRG_SPI_MISO_PORT GPIOB
// MOSI (Master Output Slave Input)
#define BNRG_SPI_MOSI_PIN GPIO_PIN_15
// NSS/CSN/CS
#define BNRG_SPI_CS_PIN GPIO_PIN_10
#define BNRG_SPI_CS_PORT GPIOF
// IRQ
#define BNRG_SPI_IRQ_PIN GPIO_PIN_0
// !!!
#define BNRG_SPI_IRQ_PULL GPIO_PULLDOWN
#define BNRG_SPI_IRQ_PORT GPIOA
  • Next thing that got me puzzled for quite a time was that after initial version query command, which seemed to work OK (I got some reasonably looking responses) the communication hanged. IRQ pin went high, and wouldn’t go low. So µC thought that there is data to read and was continuously reading and reading.
  • Only after I read in UM1755 User manual that IRQ goes HI when there is data from NRG to µC, and it goes HI-Z when there is no data, I double checked the schematics, and found that BlueNRG-dev board has no pull-down resistor in it. The ST example code I had also hadn’t have IRQ pin configured in pull-down state, so I wonder how this could work. Maybe Nucleo boards have pull-down resistor soldered there.
  • For now (after 3 or 4 nights, and I consider myself as quite experienced) I got this (see photo):Screenshot from 2016-03-21 02-09-30
  • “Iwona” is probably my neighbor’s phone.
  • Quick update made the next day morning. My Linux computer is able to connect to the test project, and query it (right now it simply counts up):

Screenshot from 2016-03-21 13-05-25

tac

Tac command outputs file in reversed order (thus its name – it’s “cat” but backwards). And sponge is a pure magical one which acts as some kind of buffer which gets all the output and then puts it into whatever file in one go, completely removing its contents (this is how I get it – may be wrong). So one could output modified contents of a file to this file itself as so:

tac diamond.dat | sponge diamond.dat

 

Bloki

Mamy na Jelonkach dwie piwnice. Do dziś był w nich potworny burdel, który posprzątałem i jeśli jeszcze nie zauważyliście, to właśnie się tym chwalę. Strasznie mnie to zmęczyło i zgłodniałem też porządnie, więc poszedłem do chińczyka hał-hała nieopodal, żeby wziąć coś na wynos. I tak sobie siedziałem na zewnątrz, pogoda ładna i tak dalej i jakiś taki dobry humor mi się zrobił. Siedziałem, patrzyłem w dal, gdzie wznoszą się ściany bloków po drugiej stronie ulicy, a nad nimi latają jaskółki. I pomyślałem sobie tak. Gdybym nie miał zupełnie nic i w ogóle nigdy bym niczego nie posiadał, to także nie znał bym takiego pojęcia jak sprzątanie. No bo co może wiedzieć o sprzątaniu ktoś, kto nic nie ma. Wyobraźmy sobie jakiegoś troglodytę, który jedyne co może posprzątać, to liście na trawniku, albo gałązki pod drzewem, ale ponieważ ani trawnik, ani drzewno nie należą do niego (bo nic do niego nie należy), to po co ma to robić. I z jednej strony szczęśliwy jest taki troglodyta, bo troglodyci są znani ze wstrętu do wiosennych porządków, ale z drugiej strony, kiedy przyjdzie na niego czas, i troglodyta zdechnie, to żadna rzecz, żaden kot w pustym mieszkaniu po nim nie pozostanie. Kiedy ja zdechnę, to zostanie po mnie mnóstwo niepotrzebnych śmieci, i ktoś może sobie coś weźmie i pomyśli o mnie chociaż. A jak cała ludzkość nagle zdechnie, to zostaną po niej te bloki, puste mieszkania, puste samochody, puste resztki czegoś, co kiedyś było cywilizacją. No i to w zasadzie to wszystko co wtedy wymyśliłem, bo żona hał-hała przyniosła mi w międzyczasie żarcie na zewnątrz i wróciłem do domu.

Przyjaciele w Radomiu

    Kiedy się ma nową rzecz, na którą się czekało długo tak jak ja, to się tą rzecz oczywiście chce przetestować, nacieszyć, pobawić. Nie inaczej było w przypadku motocykla, tej piekielnej maszyny, którą nabyłem niedawno. Nacieszenie się nowym pojazdem polega na jeżdżeniu w tą i z powrotem, czasem w jakimś celu, a czasem bez. Kiedy już objeździłem całą rodzinę, pochwaliłem się krewnym i znajomym królika i o mało co nie przeziębiłem, powstał dylemat gdzie w następnej kolejności pojechać. Kiedy motocykla nie miałem, wyobrażałem sobie go jako remedium na wszystkie swoje niepowodzenia, sinusoidalne skoki humoru, czasem też całkiem spore dołki. Wyobrażałem sobie siebie jadącego sobie w dal i pogrążonego w swoich myślach, sam na sam ze sobą, odciętego od wszystkiego. Stety lub niestety myliłem się. Okazuje się bowiem, że jadąc na motocyklu człowiek jest (musi być) dwa, lub jeszcze więcej razy bardziej skupiony na tym co się dookoła niego dzieje niż w samochodzie. Tak więc jadąc na moto miałem teraz zupełną pustkę w głowie i jakkolwiek czerpiąc wielką przyjemność z podróży, moja głowa była jednak zaprzątnięta raczej tym, żeby nie dać się zabić, niż rozmyślaniami o sensie istnienia, czy jakichś tam innych pierdół. A Jechałem do Radomia Aleją Krakowską, czy trasą krakowską, jak zwał tak zwał. Radom to paskudne miasto, które pamiętam z dzieciństwa, ponieważ spędzałem tam wakacje u babci Janki i przez babcię, oraz jego paskudność odczuwam do niego spory sentyment (nie taki jak do P-na, ale zawsze). Uzbrojony w karteczkę z numerami tras (wracałem przez Kozienice) i listą miejsc do odwiedzenia (3 pozycje, ale mam słabą pamięć, stąd lista) pędziłem sobie wesoło na południe. Udało mi się tam dojechać w około godzinę, co jednak nie było zbyt rozsądne, jak potem uświadomiły mi żona i matka, no ale cóż.

    Nie jest tak, jak twierdzą niektórzy, że motocykl jest magnesem na płeć piękną i wystarczy się polansować po mieście, żeby od razu jakiś plecak podwieźć, a potem stłamsić. To znaczy może to jest prawda, ale mnie przynajmniej jest to zupełnie obce i to dobrze. Jakkolwiek laski nie kleją się do mojej maszyny, to jednak zupełnie inaczej sprawa się ma z małymi dziećmi. Mam go już miesiąc, a rozmawiałem już chyba z pięciorgiem dzieci w wieku przedszkolnym, o dzieciach gapiących się z mniej dostępnych miejsc nie wspominając. Jeden chłopiec na przykład, lat mniej więcej na oko 2 (bo ledwo mówił) zachęcany przez swojego tatę wymieniał mi wszystkie marki motocykli jakie znał. Trzeba mu było tylko trochę pomagać z pierwszą sylabą, a potem sobie seplenił. Zuch. Inny chciał się gramolić na motocykl, ale babcia na niego nawrzeszczała, jeszcze inne dzieci po prostu przechodząc ciągnęły za klamki, manetki i inne wystające rzeczy. Zupełnie mi to nie przeszkadza.

    Brak mi niestety jeszcze doświadczenia i w Radomiu przydarzył mi się pierwszy paciak. Paciak to (chyba fachowe) określenie na wywrotkę “parkingową” kiedy to toczymy się na motocyklu w celu właśnie wspomnianego parkowania, lub w jakimś innym, dość, że prędkość mamy wtedy prawie żadną. W takich momentach motocykl jest bardzo niestabilny, bowiem brak sił żyroskopowych pochodzących od kręcących się kół (kręcą się wolno, więc siła mała i słabo nas/mnie stabilizuje). I tak samo było w moim przypadku. Zauważyłem bowiem na ulicy Niedziałkowskiego w Radomiu grupę młodzieży gimnazjalnej w sile około dziesięciu na tle wielkiej swastyki wysprejowanej na murze. Szybko się oddaliłem, ale jednak ciekawość pozostała i dokopawszy się do głęboko skrywanych pokładów odwagi postanowiłem do nich wrócić, żeby zrobić im zdjęcie. Niestety nie było ich, ja zacząłem zawracać, samochód się zjawił znikąd, ja po hamulcach i pac. Wygrzebałem się spod motocykla, łażę dookoła i patrzę co by tu zrobić (wyłączył się sam). Próbuję podnieść przodem, nie daję rady. Próbuję inaczej, też nic. Wtem słyszę ożywioną rozmowę. To właśnie owi chłopcy, których szukałem wymieniali się uwagami na temat przyczyn mojego nieszczęścia. Konkluzja brzmiała : “typ się przestraszył”. No i dużo mieli racji, moja gwałtowna reakcja była skutkiem nagłego pojawienia się zagrożenia, którego się nie spodziewałem (to był jakiś stary golf). Szli w moją stronę i zastanawiali się na głos, czy mi pomóc, czy nie. Szybko (na szczęście) doszli do porozumienia, że jednak tak (“pomożemy typowi”) i jeszcze będąc w pół drogi, uspokoili mnie (cały czas szarpiącego się z kawałem żelastwa) w te słowy : “zostaw to, bo się zesrasz!”.

    Zdjęcia już im nie zrobiłem, ale wdzięczny jestem im do dzisiaj, bo strasznie ciężkie to cholerstwo. Oczywiście nie miałem gmoli ani kraszpadów, a motocykl był nowy, więc sami sobie możecie dalej dopowiedzieć co i jak. Na szczęście straty są znikome, a na gmole i kraszpady ciągle zbieram. Pozdrawiam chłopaków z Radomia.

O żuku (2)

    Pory roku mijają, sezony się kończą, przychodzi taki czas, że uśpione drzewa budzą się do życia pobudzone pierwszymi mocniejszymi promieniami słońca. Gdzieniegdzie leży jeszcze śnieg, a już pierwsze kiełki przebijają się przez niego i wystają nieśmiało niepewne, czy jakiś nagły przymrozek nie zabije ich w mgnieniu oka. Potem następuje spokojny czas lata, kiedy rośliny boją się już tylko braku wody, ale to w dużych lasach zdarza się rzadko, no przynajmniej jeśli roślina jest sprytna i wie gdzie się wysiać. Te mądre zawsze znajdują wilgotne miejsce, gdzie nigdy nie jest sucho i nigdy nie są spragnione. Później przychodzi ponura jesień, prawie wszystko w lesie co żyje i co jest małe, albo trochę większe szykuje się do snu, czy jaki tam jeszcze ma patent na przetrwanie. Niektórzy śpią, niektórzy gubią liście, inni zagrzebują się w norkach, jeszcze inni w kokonach, prawie każdy w lesie jakoś sobie tam radzi, a w razie potrzeby zawsze może liczyć na pomoc innych. Niestety, ale cały ten cykl jest tylko marzeniem dla motyla. Jak kolorowy motocyklista na autostradzie, motyl żyje szybko, ale krótko, o czym niestety żuk jakoś zapomniał, albo w sumie nie zapomniał, tylko nie chciał pamiętać.
    Żuk nie wychodził ze szpitala od dwóch dni. Siedział przy łóżku ukochanej, a ona marniała w oczach. Kolory jej zwiewnych sukienek jakby przyblakły, a i zwiewne w zasadzie już przestały być. Motylica ufnie patrzyła na niego, a on płakał i płakał, starając się to przed nią ukryć, ale nie wychodziło, bo sam wyglądał okropnie – zapuchnięty, zasmarkany i zarośnięty. Chwilami próbował rozpaczliwie gonić czas, chwilami przywoływał te wszystkie 7 szczęśliwych dni, które spędzili razem intensywnie (to wcale nie jest śmieszne – dla robali to całkiem sporo czasu) i z radością. Jego myśli goniły się, potem potykały i gubiły, plątały, tak, że w głowie miał w zasadzie mętlik. Im dłużej tam siedział i im było gorzej, tym dobitniej uświadamiał sobie ile czasu stracił na rzeczy nie istotne, ile rzeczy bezpowrotnie przeminęło w jego życiu i że ta ostatnia, najważniejsza, także zaraz przeminie, a on nie będzie mógł już nigdy patrzeć na swoją ukochaną. “Kiedy ktoś ma 1000 par oczu jak ja, i patrzy na cierpienie bliskiej osoby, to boli go to o wiele bardziej” myślał naiwnie żuk. Było już na prawdę źle, i z nią i z nim. Cierpieli razem, ale on czuł teraz gniew i bunt. Chciał widzieć tylko ją, każda inna rzecz była zbędna, każdy inny obraz przeszkadzał i żuk bał się, że jej wizerunek zatrze się w jego pamięci, że zginie gdzieś w otchłani wszystkich tych bezsensownych i niepotrzebnych rzeczy, które widzi się każdego dnia i choćby to były dobre rzeczy, to jednak nikt nie zrozumie tego jak bardzo żuk ich w tym momencie nienawidził. A potem był już tylko ledwie słyszalny szept, którego nawet ja nie zrozumiałem, rozumieli go tylko oni, oni, którzy szeptali do siebie po raz ostatni, a potem było już tylko cicho. On ostatni raz spojrzał na nią cichą i spokojną. Nie wiem co było dalej, pamiętam jeszcze tylko, ponury dźwięk, cichy stukot, jakby dzwonienie koralików spadających na posadzkę. Wyglądały jak nasiona granatu rozsypujące się po sali. Toczyły się, wirowały, co raz więcej i więcej. Było ich 1000 par. “teraz będę Cię już widział zawsze” myślał naiwnie żuk.