Bart Slinger https://www.bartslinger.com Electronics and Software Sun, 29 Oct 2023 07:38:03 +0000 en hourly 1 https://wordpress.org/?v=6.2.6 A 340 gram airplane with 4G https://www.bartslinger.com/diy-projects/a-340-gram-airplane-with-4g/ https://www.bartslinger.com/diy-projects/a-340-gram-airplane-with-4g/#respond Sun, 29 Oct 2023 07:38:03 +0000 https://www.bartslinger.com/?p=736 Read More »]]>

Introduction

I thought it would be a cool idea to build a super small airplane that could fly long range on a 4G connection. To my surprise, I could not find anything like this on the internet. At least not as small as I wanted it to be. The closest is probably a modified Parrot Disco, weighing just under a kilogram.

Objectives

The objective is to build an airplane that has:

  • Flight time at least one hour
  • “Unlimited” range by 4G connection
  • Live video
  • GPS for waypoint missions
  • Lightweight

Technical Specifications

I settled on the ZOHD Drift because it is the smallest possible airframe I could find which would still be able to fit all the necessary components. It has nice access hatches on all sides. The model is designed for slow flying and gliding, thus should also be very efficient.

The final specifications of the aircraft where the following:

  • ZOHD Drift Airframe
  • 2x 18650 Li-ion battery
  • Raspberry Pi Zero 2W
  • ZeroCam fisheye camera
  • Huawei 4G LTE dongle
  • Mateksys F411-WTE flight controller
  • Mateksys M8Q-5883 (GPS and compass)
  • Total weight: 340 gram
  • Flight time: 1 hour 20 minutes
  • Cruise speed: 10 m/s

Build

Why not Ardupilot or PX4

The easy way to build a 4G airplane is to use an ArduPilot/PX4-capable flight controller. Then you can run something like mavlink-bridge on the Raspberry Pi and use QGroundControl or another ground station to control the flight.

However, the problem with these ArduPilot/PX4 flight controllers is that they are too big for an airplane like the ZOHD Drift. Looking at it the other way around: All flight controllers that fit in the ZOHD Drift are not compatible with ArduPilot/PX4 because they don’t have sufficient ROM.

The Matek F411-WTE flight controller fits perfectly in the ZOHD Drift, but cannot run ArduPilot/PX4.

INAV

ArduPilot/PX4 needs a lot of ROM because it is full of features that I don’t need. Essentially, the flight controller should just be able to read all the sensors and stabilize the airplane. Navigation tasks could even be outsourced to the Raspberry Pi.

The INAV software provides a nice balance and even has waypoint navigation capabilities, while remaining small enough to fit on smaller flight controllers. It is a fork of BetaFlight, but more suited towards fixed-wing and GPS navigation. However, not using Ardupilot or PX4 poses some additional challenges.

Enabling flight control via the internet

Normally, INAV can only take flight control commands from the radio link. In this project, I still use a radio transmitter for low-latency flight control. This is necessary to fly the aircraft in partially stabilized modes, such as takeoff and landing.

When the aircraft is in the air and flying in GPS assisted modes, the latency is no longer critical. In this phase of the flight, the aircraft could also be controlled via an unstable high-latency internet connection. Typical commands would be direction change or vertical speed setpoints. Even more high-level commands include: Enable/disable waypoint mission, return home or loiter.

The Raspberry Pi can send flight control commands to the flight controller via UART. To make this work with INAV, it is necessary to compile the software with the MSP_RC_OVERRIDE flag enabled. INAV provides instruction on this, but I suspect it is not often used because it triggered a compiler error.

In src/main/target/common.h, uncomment the line that says:

#define USE_MSP_RC_OVERRIDE.

To ‘fix’ the compiler error, I had to remove a (false positive) static assert in src/main/cms/cms_menu_osd.c (“missing OSD elements in CMS”).

Communication loss failsafes

Enabling MSP_RC_OVERRIDE allows me to control the plane via 4G or radio. However, by default, the airplane will activate the failsafe when the radio link is lost (even when it receives 4G commands). Since I wanted the ability to fly outside the range of the radio, I needed to modify this failsafe behavior. I made the following modification in src/main/rx/rx.c:

    if (rxFlightChannelsValid && rxSignalReceived) {
        failsafeOnValidDataReceived();
#if defined(USE_RX_MSP) && defined(USE_MSP_RC_OVERRIDE)
    } else if (IS_RC_MODE_ACTIVE(BOXMSPRCOVERRIDE) && !mspOverrideIsInFailsafe()) {
        failsafeOnValidDataReceived();
#endif
    } else {
        failsafeOnValidDataFailed();
    }

With the additional code, the updated behavior is as follows: When there is no radio signal received, the code will check if the 4G control mode is active. If that is the case, and the 4G control mode is not in a failsafe condition, the condition is now considered valid.

However, if both the radio link and the 4G connection are lost, the failsafe is activated. I configured the failsafe to be a Return-to-Home (RTH) action. Before using RTH as failsafe, it is very important to check the behavior while still in radio line of sight. If a problem happens during testing, it is then still possible to handle the situation safely.

The procedure for testing and activating failsafes should always be:

  1. Set failsafe to engine off, fixed roll angle. This prevents potential fly-aways.
  2. Test navigation modes, make sure the airplane is capable of return to home.
  3. Do all of the tuning in radio line of sight.
  4. Only when you are 100% certain that the airplane behaves correctly in all navigation modes, configure Return-to-Home as failsafe. Make sure to also check auto-landing.

When the airplane approaches the home position, you will probably be able to take back control before auto-landing starts. However, if your radio transmitter is breaks during the flight, this makes sure the flight ends as soon as possible in a reasonably controllable way.

Minimum speed behavior

While testing the GPS assisted flight modes, I observed some strange behavior. The airplane would sometimes throttle up to almost full throttle (I could clearly hear it from the ground) and back to idle after a few seconds. I noticed this especially during days with a bit of wind.

After some digging in the code, I found a hard-coded threshold of 7 m/s. If the groundspeed of the airplane would drop below 7 m/s, an integrator started winding up quickly with a high hard-coded gain. The value of this integrator would be added to the throttle command, up to a maximum of 50% on top of the cruise throttle setting. With cruise throttle at 40%, it would add up to 90% throttle.

The responsible function is applyFixedWingMinSpeedController in src/main/navigation/navigation_fixedwing.c. The idea behind this is good. If the aircraft encounters a strong headwind during a Return-to-Home action, this would make sure the aircraft maintains a minimum ground speed of 7 m/s. Without this function, the aircraft could even start flying backwards and it would never make it back home in a failsafe situation.

Actually, there is another failsafe in INAV that triggers a landing when the aircrafts distance to home keeps increasing during Return-to-Home. This prevents a fly-away but you will still lose your aircraft.

The gain of the integrator is set really high, which causes the airspeed to oscillate a lot around the minimum speed of 7 m/s. Arguably this is better than a gain that is set too low, because then the groundspeed may become negative. Since the heading of the plane is determined from ground track instead of compass, the aircraft will then also turn away from home and make the situation even worse (potentially resulting in a fly-away).

For the ZOHD Drift, the most efficient cruise speed seems to be around the 10 m/s mark. That’s probably much lower than most aircraft that use INAV. Therefore, I felt comfortable reducing the minimum groundspeed to 5 m/s. This allows me to fly in 5 m/s winds without triggering the minimum speed controller.

I also reduced the hard-coded integrator gain from 1.5 to 0.5 and did a lot of testing on days with a little bit over 5 m/s wind. There is still a little bit of oscillation on throttle/airspeed when flying into the wind, but definitely acceptable. By allowing a little bit of oscillation, we are still on the safe side.

4G telemetry and joystick control

To control the aircraft via 4G, I made a small Svelte application that I ran locally on the laptop. The application opens a websocket to a custom program running on the Raspberry Pi. The Svelte application reads the values from a joystick (a playstation 4 controller) and sends those values over the websocket. The other way around, this websocket is also used to receive telemetry data from the Raspberry Pi.

The custom program on the Raspberry Pi simply translates websocket messages to MSP commands on the UART connection to the flight controller board. The other way around, it receives telemetry from the flight controller board and sends those over the websocket.

Low-latency livestream

I configured a Janus Streaming Server which would receive a RTP H264 payload. In the Svelte application, I created a viewer. Janus works really well for low-latency video, which is essential for controlling the airplane via 4G.

On the Raspberry Pi, I would use ffmpeg or gstreamer to open the pi-cam, pack the stream into RTP and send it to the Janus server.

Tailscale

The Raspberry Pi automatically connects to the internet when the Huawei 4G dongle is connected. However the Raspberry Pi does not have a public IP address. To solve this, I installed tailscale on the Raspberry Pi and on my laptop. This sets up a virtual private LAN which allows me to connect directly with the Raspberry Pi.

4G connection stability

The connection of the 4G connection was not very stable. I was usually flying between 30 and 50 meters altitude. Even with a clear radio-line-of-sight between the airplane and the cell tower (only 300 meters away), the connection was not stable.

After a lot of testing, I noticed that the connection would be less stable when the aircraft was in a turn. Especially in loiter mode, this makes the connection really unstable. After flying straight for a couple of seconds, the connection would be stable (showing uninterrupted livestream). As soon as I started a turn, the livestream would be temporarily interrupted.

Next steps

Off course it is super cool to fly via 4G, but it would be much cooler when it would actually be allowed to fly long distance. This airplane is technically able to fly a distance of 40 km. Unfortunately I have not been able to see this airplane to its full potential.

These types of airplanes are relatively cheap to make and could for example be deployed in a swarm. The Raspberry Pi or another onboard computer could do some basic image processing, perhaps to assist in search-and-rescue or wildfire monitoring.

I am also intrigued by the idea of scaling up. I’ve seen examples on the internet of more than 10 hours flight time using a 2.4 meter wingspan Phoenix 2400. I imagine a couple of drone airports where these planes are deployed from. The airplanes can do observation missions or respond to emergencies. With a 4G internet connection, they can cover large areas. Only very few drone airports are required. The planes then need a runway or grass strip to land, but that might be acceptable if it can stay airborne for 10 hours. I think this would be an interesting alternative to drone-in-a-box systems, especially for situations where a large area needs to be covered.

]]>
https://www.bartslinger.com/diy-projects/a-340-gram-airplane-with-4g/feed/ 0
Over-engineering a Mun landing in Kerbal Space Program https://www.bartslinger.com/kerbal-space-program/over-engineering-a-mun-landing-in-kerbal-space-program/ https://www.bartslinger.com/kerbal-space-program/over-engineering-a-mun-landing-in-kerbal-space-program/#respond Sat, 30 Jan 2021 20:19:27 +0000 https://www.bartslinger.com/?p=662 Read More »]]>
This is an over-engineered attempt to perform a very efficient Mun landing, which I worked on together with my friend Joost Meulenbeld. Using kRPC, we have written an autopilot that executes the landing automatically.

https://youtu.be/lqx7oWW7va8

Methodology

For a perfectly spherical body, the ideal trajectory would be to place the periapsis just centimeters above the surface. An almost horizontal suicide burn then kills of all horizontal velocity. However, Mun is not a perfectly spherical body and this approach would risk us to fly into a mountain. The varying mass also prevents us from making a simple calculation.

Our method is to first generate a reference approach profile. We built a simulator that implements the orbital mechanics. Instead of starting the simulator in orbit and simulating our way towards the surface, the simulation is started on the landing spot. The fuel mass is increasing in this ‘reversed’ simulator, but everything else is the same. The simulator starts with a constant pitch angle of 10 degrees and 90% throttle. The angle can be lowered further, depending on obstacles on the surface of Mun. 90% throttle is chosen to leave some margin for control, as we want to do a precision landing of course.

The ‘reversed’ landing simulator

The state vector for the simulator is:

(1)   \begin{equation*} \mathbf{x} = \begin{bmatrix}r \\ \dot{r} \\\theta \\\dot{\theta} \\m_{fuel}\end{bmatrix}\end{equation*}


Where r is the radius from the center of Mun and \dot{r} is vertical speed. \theta is the angle relative to the landing location at the landing time and \dot{\theta} is the angular velocity. m_{fuel} is the remaining fuel mass.

The differential state equations are:

(2)   \begin{equation*}  \dot{\mathbf{x}} = \begin{bmatrix} \dot{r} \\ \sqrt{\frac{GM}{r^2}} + r\dot{\theta}^2 + \frac{T_{max}}{m} u_t\sin{\phi}  \\ \dot{\theta} \\ -\frac{2\dot{r}\dot{\theta}}{r} + r\dot{\theta}^2 + \frac{1}{r}\frac{T_{max}} {m}u_t\cos{\phi} \\ u_{t} \cdot \dot{m}_{max} \end{bmatrix}\end{equation*}


\dot{m}_{max} is positive! Mass is increasing in this ‘reversed’ simulator. u_t is throttle, T_{max} is the thrust delivered at 100% throttle and \dot{m}_{max} is the mass flow at 100% throttle. \phi is the pitch angle relative to the horizontal direction. m is the vehicle mass, which is the sum of the vehicle fuel mass m_{fuel} and dry mass m_{dry}. G and M are the gravitational constant and mass of Mun.

The differential equations are converted to python and are solved by the solve_ivp function of scipy, with time steps of 0.1s.

Getting to orbit

Now that we have a ‘reversed’ simulator, the task is to find a set of control inputs that results in a circular orbit at 10 km above the surface.

  1. Apply 90% throttle at 10 degrees pitch, until the apoapsis reaches 10 km above the surface.
  2. Coast to the apoapsis.
  3. Perform a circularization burn at apoapsis.

For step 1, we need to know the apoapsis of the orbit in order to stop burning in time. From the simulator, we only have the state vector which does not contain orbit parameters. The following calculations allow us to calculate orbit parameters just from r, \dot{r} and \dot{\theta}:

(3)   \begin{equation*} V = \sqrt{\dot{r}^2 + \left(r\dot{\theta}\right)^2}\end{equation*}

(4)   \begin{equation*} \gamma = \arctan{\frac{r\dot{\theta}}{\dot{r}}}\end{equation*}

(5)   \begin{equation*} a = \left(\frac{2}{r} - \frac{V^2}{GM} \right)^{-1}\end{equation*}

(6)   \begin{equation*} e = \sqrt{1 - \frac{r^2V^2 \sin{\gamma}^2}{GM \cdot a}}\end{equation*}

(7)   \begin{equation*} r_a = a \left( 1 + e\right)\end{equation*}

(8)   \begin{equation*} r_p = a \left( 1 - e\right)\end{equation*}

V is the length of the velocity vector, \gamma is the flight path angle, a is the semi-major axis of the ellipse and e is the eccentricity of the ellipse. r_a and r_p are the radius at apoapsis and periapsis respectively. \gamma is positive when approaching the apoapsis and negative when approaching the periapsis. Using these parameters and the current radius we can determine the position on the ellipse.

(9)   \begin{equation*} \cos{E} = \frac{1}{e} - \frac{r}{ae}\end{equation*}

(10)   \begin{equation*} E = \text{sgn}\left(\gamma\right) \cdot \arccos\left( \frac{1}{e} - \frac{r}{ae}\right)\end{equation*}

(11)   \begin{equation*} M = E - e\sin{E}\end{equation*}

(12)   \begin{equation*} \nu = \arccos{\frac{\cos{E} - e}{1 - e\cos{E}}}\end{equation*}

(13)   \begin{equation*} n = \sqrt{\frac{GM}{a^3}}\end{equation*}

E is the eccentric anomaly, M is the mean anomaly and \nu is the true anomaly. n is the mean motion in [rad/s]. Using these equations, we can calculate the time to apoapsis and velocity at apoapsis V_a:

(14)   \begin{equation*} \left( t_a - t \right) = \frac{\pi - M}{n}\end{equation*}

(15)   \begin{equation*} V_a = \sqrt{GM\left( \frac{2}{r_a} - \frac{1}{a}\right)}\end{equation*}

The required orbital velocity for a circular orbit at radius r is given by:

(16)   \begin{equation*} V_c = \sqrt{\frac{GM}{r}}\end{equation*}

The duration of the cirularization burn is estimated using a constant mass:

(17)   \begin{equation*} \Delta t_{burn} = \left(V_c - V_a\right) \frac{T_{max}}{m}ut\end{equation*}

For the circularization burn, a throttle setting u_t of 50% was used. The burn starts \Delta t_{burn}/2 seconds before reaching apoapsis.

Iterate to find correct fuel mass

The reversed simulator needs to start with an unknown quantity of fuel. The initial run can start with 0 kg. This will result in a certain amount of fuel in circular orbit. The amount of landed fuel is iterated a couple of times until the the orbit fuel mass matches with the fuel on board the ship.

The flight profile is now complete. Altitude and throttle are plotted here against time:

Compensate for rotation of Mun

The angle \theta where we need to start the de-orbit burn is relative to the landing location at t = 0. However, the landing location is moving with approximately 9m/s because Mun rotates. That’s more than 2km error if we don’t compensate. If the de-orbit burn starts at t_c. The longitude at which we need to start the first burn is:

(18)   \begin{equation*} \lambda_{start} = \lambda_{target} - \theta_{start} + \omega t_c\end{equation*}

where \omega is the rotational velocity of Mun in [rad/s].

Feedback control in the landing burn

The de-orbit burn is purely feed-forward. This needs to be timed precisely. A tiny deviation in the de-orbit burn can result in a large error at the start of the landing burn.

The reference trajectory describes the desired vehicle state for every moment in time. However, there will be small deviations from this profile that need to be corrected for. Since Mun itself is rotating, we must aim for the landing to happen at t=0. Every second difference increases the error by 9 meters. The variables we try to control are longitude and radius (altitude). A relatively simple controller is implemented that decouples these. To control longitude, we use thrust. For altitude control, we use the pitch angle.

Full video of the landing burn

Longitude control

The longitude error is defined by

(19)   \begin{equation*} \lambda_e = \lambda_{ref}\left(t\right) - \lambda\end{equation*}

where \lambda_{ref}\left(t\right) is the longitude in the reference trajectory at a given time and \lambda is the longitude of the ship. This is converted to a horizontal distance error, which is more practical for control.

(20)   \begin{equation*} x_e = \lambda_e \cdot r\end{equation*}

The throttle setpoint is defined by the following PD controller with feed-forward from the reference trajectory:

(21)   \begin{equation*} u_t = u_{t,ref}\left(t\right) - P_x \cdot x_e - D_x \cdot \frac{d}{dt}x_e\end{equation*}

The gains P_u and D_u are found kerbal-style by tuning. \u_t is bound between 0.8 and 1.0 (the reference trajectory has a constant value of 0.9).

Altitude control

The altitude error is:

(22)   \begin{equation*} h_e = r_{ref}\left(t\right) - r\end{equation*}

The pitch angle setpoint is controlled with the following PD-controller with feed-forward from the reference trajectory:

(23)   \begin{equation*} u_{\phi} = u_{\phi,ref}\left(t\right) + P_h \cdot h_e + D_h \cdot \frac{d}{dt} h_e\end{equation*}

These gains are also tuned kerbal-style, and the pitch setpoint is bound between 0 degrees and 20 degrees. The reference trajectory is 10 degrees.

Horizontal ‘suicide burn’

For the last 1000 meters, a different horizontal controller is used for improved landing accuracy. It is somewhat similar to a suicide burn, except that it requires slightly less than 100% throttle. When we are this close to the target, it is safe to ignore orbit dynamics and pretend that Mun is flat.

If the following constant horizontal acceleration setpoint \ddot{x}_{sp} would be applied, the horizontal velocity reaches zero exactly at the target:

(24)   \begin{equation*} \ddot{x}_{sp} = \frac{1}{2}\dot{x}^2 \cdot \frac{1}{x}\end{equation*}

where \dot{x} is the horizontal velocity and x is the horizontal distance to the target. The required horizontal acceleration can be translated to a horizontal thrust setpoint:

(25)   \begin{equation*} u_t = \ddot{x}_{sp} \cdot \frac{m}{T_{max}}\end{equation*}

Technically we would need a little more thrust to compensate for the pitch of the vehicle. Also, the target height is increased by 5 meters so that we can quickly rotate the vehicle upright for a smooth vertical touchdown.

What’s next

This post just shows the part where we get from orbit to the surface of the Mun, but we have already automated the entire flight starting at launch from Kerbin. The next step will be to build a Mun base that mines for fuel. Instead of landing on the surface, we can build vehicles that automatically dock to the Mun base and don’t require landing legs. Then, we can transfer massive amounts of fuel from the Mun base to an orbital station, where we can refuel the biggest rockets and spaceships to send to the edge of the Kerbol System and beyond!

Source code

For those who are interested: the code is open-source, but not documented.

https://gitlab.com/joost.meulenbeld/ksp_calculator

https://gitlab.com/joost.meulenbeld/ksp_interface

]]>
https://www.bartslinger.com/kerbal-space-program/over-engineering-a-mun-landing-in-kerbal-space-program/feed/ 0
STM32 CubeMX “Timer + ADC + DMA” https://www.bartslinger.com/stm32/stm32-cubemx-timer-adc-dma-configuration/ https://www.bartslinger.com/stm32/stm32-cubemx-timer-adc-dma-configuration/#comments Mon, 14 Jan 2019 23:40:28 +0000 https://www.bartslinger.com/?p=643 Read More »]]> This is a simple write-up so I don’t have to figure it out again next time. The goal is to setup a periodic timer which starts an ADC measurement on the background using DMA. I’m using a NUCLEO-F303RE development board.

Timer configuration

The STM32F303RE is configured to put 72MHz at TIM4. To generate events at 10Hz, a prescaler of 7200-1 is used with a counter period of 1000-1. After the prescaler, the frequency is 10kHz. The counter reaches 1000 after 0.1 second, after which the value is reloaded. An important setting here is the “Trigger Event Selection TRGO: Update Event”. This will be used to trigger the ADC.

ADC configuration

I have selected 5 channels (IN3 – IN7) on ADC2 which should all be measured in one go.

In the ADC configuration, I use 12-bit resolution. DMA Continuous Requests is enabled, because otherwise it will only measure on the first trigger from the timer. The number of conversions is set to 5, because I am measuring 5 channels. Each channels is configured individually in the ‘Rank’-dropdown.

The External Trigger Conversion Source is set to “Timer 4 Trigger Out Event”, which corresponds to the previously mentioned timer configuration. This way, the ADC will be started every 0.1 seconds.

To do this using DMA, the DMA is configured as follows. Data Width is chosen Half Word (16-bits), since the ADC is configured at 12-bit. Mode Circular is enabled because otherwise only one measurement will be done. Values of the previous measurement are overwritten.

Code

After generating the code with CubeMX, the following lines are important. First of all, a buffer for DMA:

/* USER CODE BEGIN 0 */
volatile uint16_t adc_buffer[5] = {0};

Just before the main loop, start the timer and DMA:

/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&adc_buffer, 5);
HAL_TIM_Base_Start_IT(&htim4);

Somewhere down in main.c, add the interrupt function for a completed ADC conversion:

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
  /* This is called after the conversion is completed */
}

The complete code is available on GitHub.

]]>
https://www.bartslinger.com/stm32/stm32-cubemx-timer-adc-dma-configuration/feed/ 9
Bluetooth rotary phone https://www.bartslinger.com/esp8266/bluetooth-rotary-phone/ https://www.bartslinger.com/esp8266/bluetooth-rotary-phone/#comments Fri, 31 Aug 2018 16:11:41 +0000 https://www.bartslinger.com/?p=617 Read More »]]> The idea of this project is simple. I am turning a useless classical rotary phone into a fully functional device. These phones originate from an era where devices were built to last a lifetime. However, the necessary infrastructure has disappeared over the years, rendering these phones useless.

By adding bluetooth to the rotary phone, any smartphone can connect to it and use it in the same way as a carkit. You can have a fashionable functional rotary phone on your desk and use it to make and answer calls, while your smartphone stays in your pocket. Who doesn’t love that classic ringing sound?

The bluetooth module

I considered several bluetooth modules, but mainly the Chinese XS3868 and the RN-52 from microchip. The XS3868 is cheap but the documentation is terrible. When I ran into problems I wanted to switch to the RN-52, but I couldn’t find a supplier that had them in stock, so I decided to stick with the XS3868.

I had used the XS3868 before in the Heineken speakerkratje where it functioned perfectly as a bluetooth speaker. However for the rotary phone, I also needed the UART interface in order to send commands (pick up phone, dial number, etc). The UART interface worked fine just after startup of the module and I could play music and initiate calls, but every time after 10-30 seconds, the UART interface would stop responding.

Initially I thought it could have something to do with the stability of the power supply, so I tried adding caps and different voltage sources. I also tried a voltage level shifter on the UART lines. I tried a pull-up resistor on the reset pin. I even tried a new XS3868 module, suspecting that the other one was broken. But every time I ran into the same problem, the UART interface would stop responding.

The first clue I found was the current consumption going down after losing the UART interface. Thanks to this instructable I found that the device had a deep-sleep mode and that it could be disabled. This is done by changing register ‘lowpow_clk’ from 0x1e to 0x12. For details about this register, check out the datasheet.

Speaker and microphone

Next up was to connect the microphone and speaker to the bluetooth module. The speaker turned out to be really easy. I just had to connect the two speaker wires from the phone to the Agnd and Aleft pins (could also have used Aright).

The microphone was not so simple. I first hooked it up according this vague diagram found on the internet:

Microphone hookup diagram.

However I didn’t have a 2.2uF capacitor in stock, so I used 2 1uF caps in parallel. It did not work well. The sound quality was terrible. I tried the same circuit with a small electret microphone which I had in stock and the sound quality was much better. I also figured out that if I would whisper in the original microphone, the sound quality was better. I suspected that with the original microphone, I was hitting the saturation continuously.

As a quick-fix to lower the microphone output, I tried to reduce the resistor value (2.2K) by adding more resistors in parallel. By doing so, I did not have to remove the resistors. Eventually I ended up with three resistors, of which the equivalent value is somewhere below 100 ohm. Also I added a 0.1uF capacitor but that didn’t seem to have any effect. The sound quality is now pretty good, but you still need to keep a calm voice while speaking, to avoid the saturation limit.

Microphone circuitry, ugly but functional.

Dial interface

What is a rotary phone without the possibility to dial a number? Luckily there is quiet a bit of information on the internet how this works. Basically when the dial rotates back into position, it generates a number of pulses according to the dialed number. This is simply read out with a micro-controller, in this case an ESP8266.

ESP8266 as microcontroller

The phone needs a microcontroller for a couple of functions:

  • Pickup/Hangup switch
  • Read input from the rotary dial
  • Generate pulses for ringing the bell
  • Communicate over UART with the bluetooth module

There are dozens of options for a microcontroller that can perform this task. An Arduino would definitely have been up to the task. I also considered an STM32F103 ‘blue pill’ board. But eventually I went for an ESP8266 module, because it would allow me to add WiFi to the phone.

ESP8266 module in place.

At the moment, WiFi is only used for over-the-air (OTA) firmware updates, but it could also be used for example to define hotkeys. The OTA update works as follows: If the phone is powered up with the horn down, it boots in normal mode. If the horn is picked up while powering up, the ESP goes into another mode where it connects to a local WiFi network. Thereby it is possible to update the firmware in the phone without opening it up. I know, I could also have wired the USB data pins, but I didn’t.

Power

The phone is powered from a 5V micro-USB port at the back. The ESP8266 and the bluetooth module require a 3.3V supply, so I added a small buck converter.

Buck converter for 3.3V supply.

For the ringing circuit, the electromagnet needs a higher voltage. I added a XL6009 boost convert to take 5V up to 30V. I wasn’t sure of the voltage drops when the ringing circuit is active, so I added the biggest capacitor I could find to smooth it out.

Boost converter and ringing circuit.

Ringing circuit

To ring the bells, a voltage of at least 20VAC needs to be applied at a frequency of around 25Hz. The voltage is supplied by the boost converter, but this is DC. To alternate the current, an H-bridge is required.

I couldn’t find many small H-bridge modules, but there is an overload of small stepper motor control modules available. A stepper motor controller has two H-bridges internally. I used the A4988 StepStick module. The bell is simply hooked up to one of the stepper motor phases. The DIR pin is fixed to GND. Every second rising edge on the STEP pin results in a bell being ringed. By simply applying a pulse train on the STEP pin, the ringing signal is created.

Finished all the wiring. What a mess.

Software

Because the ESP8266 is super easy to program with Arduino IDE, I used this. The code is not pretty, but functional. Code is available on github. It also contains some (succesful) experiments with state machines.

]]>
https://www.bartslinger.com/esp8266/bluetooth-rotary-phone/feed/ 4
IOTA transactions with TREZOR hardware https://www.bartslinger.com/iota/iota-transactions-with-trezor-hardware/ https://www.bartslinger.com/iota/iota-transactions-with-trezor-hardware/#comments Sat, 04 Nov 2017 15:32:32 +0000 https://www.bartslinger.com/?p=586 Read More »]]>
IOTA on trezor hardware.

About three months ago I first heard about IOTA and I really liked it. However, it is still in a very early beta stage, and lots of improvements still need to be built. One of the missing features at the moment is an offline wallet.

Another project I started with my brother a little over four months ago was to built our very own trezors. We discovered that trezor makes all their software and hardware open source. It seemed like a cool idea to order some PCB’s and components and construct our very own trezors for lower cost. Also, the device is built around an STM32, which I’ve covered many times before on my website. It did not seem to difficult to make modifications to the firmware.

The hardware

The schematics and board layout for a trezor are available on Github. However, R6 on this schematic shoud not be placed! Initially we had this resistor on the board, but that gives a lot of trouble with the USB discovery, especially on Windows. The standard board layout provides a breakout for the ST-Link programmer:

Trezor clone with ST-Link programmer attached.

Bootloader

For this development board, I did want to have some flexibility with the bootloader. By default, the trezor bootloader makes sure that the bootloader cannot be erased by enabling write protection. I disabled this for my development board. Also, it checks the signature of the firmware which I am skipping at the moment.

 

IOTA Seed generation from 24 words mnemonic

By default, the trezor uses a 24 words mnemonic which deterministically generates addresses for various crypto’s. The trezor internally generates a 64-byte long ‘seed’ according to the BIP-0039 standard. IOTA seeds consist of 81 trytes. Deterministic conversions can done between 81 trytes and 48 bytes. Kerl actually does this exactly. But 48 bytes is not 64 bytes, but 3/4 of 64 bytes. I implemented a simple algorithm that chops the 64 bytes from the mnemonic into 4 48-byte arrays. These are then absorbed by kerl and in the end the deterministic seed is squeezed out.

[edit] I got some useful feedback on this. I will change the method to implement BIP-0032 and BIP-0044.

 

Interaction with IOTA functions on the trezor

At the moment, I only implemented function calls using trezorctl, which is a python script. The basic functions at the moment are:

trezorctl iota_get_address [-i <seed_index>]
trezorctl iota_show_seed
trezorctl iota_sign_transaction -a <receiving_address> -b <balance> -v <transfer_value>

 

Address generation and transaction signing

The generation of public addresses is probably the slowest. Each address takes about 12 seconds to generate. Therefore, once the public address is generated, it is stored in the trezors flash memory.

Transaction signing is very basic at the moment. It makes the following assumptions:

  • The current address is the address holding the value.
  • The next address is the remainder address.
  • The security level is always 2.
  • The bundle always consist of 4 transactions, of which the first one sends value to a receiving address.

The trezor will respond to iota_sign_transaction with the bundle hash (which can be used for verification) and two signatures.

Generating IOTA address on trezor hardware.

 

Attaching the transaction to the tangle

This part still needs to be built properly. For proof of concept, I basically forged the transaction manually using the javascript library. I manually constructed a bundle like this:

bundle.addEntry(1, to_addr, 10, tag, timestamp)
bundle.addEntry(1, from_addr, -16000000, tag, timestamp)
bundle.addEntry(1, from_addr, 0, tag, timestamp)
bundle.addEntry(1, remainder_addr, 15999990, tag, timestamp)
bundle.finalize()
bundle.addTrytes([])

After that, I overwrote the signature/message field with the signatures received from the trezor. Attaching to tangle was done with api.sendTrytes(). Proof of work is not done on the trezor. The seed never left the trezor! The very first trezor-signed transaction ever in the tangle can be found here on thetangle.org. Thanks to the organizers of the first dutch IOTA meetup for providing me with initial funds on my trezor.

 

What’s next?

I have no contact with the trezor development team (yet). It would be cool to have a more mature version of this being merged into the official trezor firmware. Before that I’ll probably already publish some binary which can be flashed onto an official trezor. However you’ll have to deal with the “unofficial firmware detected” warning.

I have not even started yet with a computer-side program, aside from the python command line interface. It would be cool to implement this functionality in future IOTA wallets. However I’m not that much of a javascript programmer, so help would be appreciated.

It would also be good to have the source code tripple-checked for possible implementation errors. I tried to prevent bugs in the core protocol by applying the test-driven-development method, but I cannot (yet) guarantee that your IOTAs are 100% safe on the trezor.

Please note that I do all of this development as a hobby project in my spare time. Don’t ask me when something is ready or when it will be released. If you’re a programmer and you really want to have this, feel free to browse to the source code on github and compile the stuff for yourself. Otherwise, remain patient and be excited 😉

If you really love this, feel free to donate IOTA to the following address:
JBKIFPLXDQGPAOA9IHSHHYPQGNHANBOHOEZDHSEXRSJAOHCJ9YTUDVNMFZMGCOXOVJIJRXSFKYOXILCZYFK9KITSUZ
This is the same address as the one I transferred some IOTA to with my trezor.

]]>
https://www.bartslinger.com/iota/iota-transactions-with-trezor-hardware/feed/ 17
STM32CubeMX, CMake and QtCreator https://www.bartslinger.com/qtcreator/stm32cubemx-cmake-and-qtcreator/ https://www.bartslinger.com/qtcreator/stm32cubemx-cmake-and-qtcreator/#comments Wed, 08 Feb 2017 11:58:41 +0000 https://www.bartslinger.com/?p=556 Read More »]]> This is a short walk-through to setup a build configuration for STM32 projects using cmake and QtCreator.

STM32CubeMX on Linux

The STM32CubeMX software is available for Linux now! (last time I checked it was only for Windows). Download and install it. Then, click “New Project” and select the processor or board that you want to use. In my case, it’s the STM32F0 Discovery board.

Click OK and you will see a nice graphical display of your processor. You can change/tune a lot of stuff here, but for now leave it as it is. Go to Project->Settings and enter a project name. Also choose toolchain “Other Toolchains (GPDSC)”.

On the “Code Generator” tab, make sure to only add necessary library files.

After configuring these project settings, go to Project->Generate Code. If it is the first time, it will automatically download the required HAL package and install it to ~/STM32Cube/Repostory. The project folder will look similar to this:

Compiler

Previously I used the team-gcc-arm-embedded ppa, but that’s now suddenly at version 6 and I still want to use version 5. Version 5 is available from launchpad. I just copied the folder into my home directory, so I got /home/bart/gcc-arm-none-eabi-5_4-2016q3.

CMake

As you can see, there is no makefile or whatsoever in the project directory. This time, I’ll use cmake to build the project. There is an awesome project out on github which makes life super-easy: stm32-cmake. Clone the repository to your preferred location. I cloned it to /home/bart/git/stm32-cmake.

The next thing to do is add a file CMakeLists.txt in the main directory of your project, for example /home/bart/testdir/stm32f0_example_project/CMakeLists.txt. For me it has the following content:

SET(STM32_CHIP STM32F051R8)
SET(STM32Cube_DIR /home/bart/STM32Cube/Repository/STM32Cube_FW_F0_V1.7.0)
SET(TOOLCHAIN_PREFIX /home/bart/gcc-arm-none-eabi-5_4-2016q3)
SET(CMAKE_TOOLCHAIN_FILE /home/bart/git/stm32-cmake/cmake/gcc_stm32.cmake)
SET(CMAKE_MODULE_PATH /home/bart/git/stm32-cmake/cmake)

PROJECT(stm32f0_example_project)

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ENABLE_LANGUAGE(ASM)

FIND_PACKAGE(CMSIS REQUIRED)
FIND_PACKAGE(STM32HAL COMPONENTS gpio tim REQUIRED)

INCLUDE_DIRECTORIES(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMSIS_INCLUDE_DIRS}
    ${STM32HAL_INCLUDE_DIR}
    Inc
)

SET(PROJECT_SOURCES
    Src/main.c
    Src/stm32f0xx_hal_msp.c
    Src/stm32f0xx_it.c
    Inc/main.h
    Inc/stm32f0xx_hal_conf.h
    Inc/stm32f0xx_it.h
)

ADD_EXECUTABLE(${CMAKE_PROJECT_NAME} ${PROJECT_SOURCES} ${CMSIS_SOURCES} ${STM32HAL_SOURCES})

STM32_SET_TARGET_PROPERTIES(${CMAKE_PROJECT_NAME})
STM32_ADD_HEX_BIN_TARGETS(${CMAKE_PROJECT_NAME})

You may need to change some of the lines to match your processor, installation locations and source files.

QtCreator

The next and final step is configuring QtCreator. I assume you already have the newest version installed. The setup is very similar as in a previous post. However this time it is important to clean the CMake configuration field!

Then, when this is configured, go to File->Open File or Project. Browse to your project folder and select CMakeLists.txt. A screen pops up where you have to select the build configuration which you have just created:

Then hit “Configure Project” and you are all setup!

Hit the hammer in the bottom left corner to build. The default build directory will be one level higher than your project directory. In my case, the build directory is at /home/bart/testdir/build-stm32f0_example_project-STM32-Default.

OpenOCD

This process is still similar to the previous post. However there are some actions I had to take to make it work with the newest version of openocd (0.10).

  1. Download openocd from sourgeforce.
  2. Extract the archive to a directory of your choise.
  3. Install the libusb 1.0 dev package. Without this, the STLink did not work! “sudo apt install libusb-1.0-0-dev”
  4. Run “./configure” (not as root)
  5. Run “make”
  6. Run “sudo make install”
  7. “sudo cp contrib/60-openocd.rules /etc/udev/rules.d/”
  8. “sudo udevadm control –reload-rules”

It is also working in pipe mode now! So you don’t have to run openocd yourself in the background.

]]>
https://www.bartslinger.com/qtcreator/stm32cubemx-cmake-and-qtcreator/feed/ 1
Alarm clock for coffee https://www.bartslinger.com/diy-projects/alarm-clock-for-coffee/ https://www.bartslinger.com/diy-projects/alarm-clock-for-coffee/#respond Thu, 19 Jan 2017 15:21:27 +0000 https://www.bartslinger.com/?p=522 Read More »]]> The goal of this project is to make a nice interface to my coffee-machine. It is already equipped with an ESP8266 from an earlier project. The most sensible approach would be to build an app for my smartphone, or integrate it with some existing home-automation platform. Instead of an app, I want to have a physical control interface with lights and switches. Eventually the idea is to have a big home(-automation) control panel. The coffee alarm will be the first subsystem.

The coffee-machine

The coffee-machine is a very basic one, which comes with the classic sputtering sound when the coffee is almost done. This is one of the nicest sounds to wake up with, especially when combined with the smell of fresh coffee. On the inside of this machine is a lot of empty space. A long time ago, I managed to fit in an old Nokia charger for 5V, an ESP8266 NodeMCU board and a relay. I am able to control the relay from the network using the ioESP library. Unfortunately I screwed up (literally) by accidentally replacing one of the normal screws with a one-way screw. Therefore I’m not (easily) able to access the ESP and update the firmware inside the machine.

The PCB from SeeedStudio

I wanted to use the PCB as the front panel. The challenge was to have no visible traces on the front. Because it is almost impossible, I decided to put the TM1637 display driver on the front, which looks kind of cool. All the via’s are placed underneath the chip. Crossing traces without using via’s was solved by using zero ohm resistors.

The PCB was designed in KiCad and ordered from SeeedStudio. The reason to go with SeeedStudio was because they are cheaper than my favorite supplier itead when you decide to use another color than green. It was not as good as expected. They decided to put a serial number on the front of the PCB and they also treated some of the holes as via’s by poring copper in them. I ordered 5 PCB’s and it seems arbitrary which ones they added copper to. Also two of them were slightly damaged as can be seen in the picture.

Two on the left have damage. PCB on the right has copper inside some holes.

 

The 7 segment display

Attaching the 7 segment display to the PCB with connections on only one side of the board is a little bit of a challenge. I figured out that a nice way of doing it would be to use SMD pin headers. in combination with a little piece of proto-board. This would hold the display firmly attached to the PCB as well.

SMD pin headers to connect the 7-segment display.

A piece of proto-board connects the 7-segment display to the pin headers.

Well that looks nice, but unfortunately I screwed up in the PCB design by mirroring the connections. The fix is ugly but functional.

Fixed the mirrored connections on the 7-segment display.

 

The LED’s

Another thing that I screwed up are the LED’s. The initial idea was to use nice panel-mount LED’s from banggood. The title clearly says ‘6mm’ and I probably also measured this before designing the PCB. However I still screwed up (again!) by making the holes 5mm instead. However that did not defeat me. Some of the traces were to close to the hole so it was not an option to make the holes bigger myself. However I did have a big pile of traditional LED’s left, which are exactly 5mm. With some glue they remain nicely in place.

The rotary encoder

And there is more that I screwed up. The circuit below I simply copied from somewhere on the internet. The increment and decrement channels of the rotary encoder are equipped with (a wrong implementation of) a low-pass filter to prevent spikes to the micro-controller. This was simply fixed by removing R7 and R9, which was possibly because the breakout-board of my rotary encoder already had pull-up resistors on it.

Rotary encoder without low-pass filter on the pushbutton.

The knob can also be pressed, but I did not think of equipping that with a low-pass filter. However the fix was relatively simple because I had some zero ohm resistors. I simply replaced R14 with a 10k resistor and added a capacitor on top of R23. Coincidentally there was a component next to it which was attached to ground, which I used to connect to other side of the capacitor.

Low-pass filter added.

 

Banana Pi

I will talk about the software for the Arduino and Banana-pi in another post. The banana-pi basically interfaces with the coffee-machine and the Arduino on the clock.

Backside of the finished coffee-clock.

Banana Pi together with the coffee-clock in a carton box.

The following video shows how the interface is operated at the moment.

]]>
https://www.bartslinger.com/diy-projects/alarm-clock-for-coffee/feed/ 0
Portable Soldering Station https://www.bartslinger.com/diy-projects/portable-soldering-station/ https://www.bartslinger.com/diy-projects/portable-soldering-station/#comments Sat, 05 Nov 2016 11:57:25 +0000 https://www.bartslinger.com/?p=513 Read More »]]> In previous posts, I already showed the making of a soldering station. Now I’ve taken it to the next level and made a battery-powered one. The result is a very concise box which can easily be taken for soldering jobs in the field.

The case

I found a cheap aluminum case at banggood. It had just enough space to fit all the stuff.

Black aluminum case from Banggood
Black aluminum case from Banggood

My brother helped me with the milling for the TFT screen and the connectors and buttons.

Milling
Milling

Battery voltage meter

Since this device is going to be powered from a 3S LiPo battery, it would be useful to have an indication of the battery level. I constructed a simple voltage divider using a 470K and a 300K resistor. This scales 12.833 volt to approximately 5V, which is the maximum level for the Arduino analog pin A0.

Voltage divider added to the PCB using 470K and 300K resistors.
Voltage divider added to the PCB using 470K and 300K resistors.

Wiring

All the wires are soldered directly without using connectors. I also modified the standby functionality, which is now operated by a momentary push button. First, I mounted the screen in the case. Then, I pulled the power wire through the case and soldered it. Also, to make sure there is no contact with the case anywhere, I covered most of the exposed contacts in electrical tape and also put a piece of carton underneath the PCB.

All the stuff that has to go in the tiny box.
All the stuff that has to go in the tiny box.

Code

I slightly modified the code of the non-portable station. It now adds a voltage display and toggles the standby-mode with the momentary push-button. Also, the TFT screen was 1 or 2 pixels smaller than the previous one I had, so some of the text was shifted slightly to the left. The code is available in a branch on my Github repository.

It works

Look, I’m soldering:

Portable soldering station with a 2200mAh 3S LiPo battery
Portable soldering station with a 2200mAh 3S LiPo battery

 

]]>
https://www.bartslinger.com/diy-projects/portable-soldering-station/feed/ 2
PixRacer on Blade 130X Helicopter https://www.bartslinger.com/px4/pixracer-on-blade-130x-helicopter/ https://www.bartslinger.com/px4/pixracer-on-blade-130x-helicopter/#respond Tue, 09 Aug 2016 10:58:13 +0000 https://www.bartslinger.com/?p=495 Read More »]]> The ultimate goal of this project is to make a fully autonomous Blade 130X helicopter using PX4 firmware. The autopilot of choice is the PixRacer, because the form-factor is relatively small and it’s the next generation pixhawk autopilot.

PixRacer

The autopilot is hard-mounted on a wooden plate, milled by my brother. The plate clamps onto to the tail-boom and a tie-wrap holds it in place. This is by far not the nicest solution, but it’s good enough for the prototype. At this point I was still not sure if it was going to work at all. The ESP8266 on top is used for communication with QGroundControl, which works out of the box.

I removed the servo-headers which are normally attached to the PixRacer. I also removed the JST connectors of the servo’s and soldered the wires directly to the PixRacer.

PixRacer mounted on Blade 130X
PixRacer mounted on Blade 130X

Power

The battery is a 2S 450mAh LiPo. The autopilot needs 5V for operation, which is provided by the ACSP5 module which I bought together with the PixRacer. Later I figured out that the servos need 3.8V, which I did not have yet. Because it’s still a prototype, I used a small 3A buck converter from ebay to provide power to the servos.

Power boards
Left: buck converter for servos. In the yellow tape: ACSP5

Motor controller

The ESC I used is a Turnigy Plush 12A. I have not yet configured it with BLHeli, but I will need a governor mode at some point. The ESC actually also has a BEC which could be used instead of the ACSP5, but I realized that too late :-p.

Turnigy Plush 12A
Turnigy Plush 12A

RC Transmitter / Receiver

I have some experience with DSMX receivers and transmitters, but for this project I decided to go with FrSky stuff. I have a Turnigy 9X transmitter with a FrSky DJT module. The receiving side is a RX-F802 which I got from banggood. It’s hard to find documentation about this module, but apparently it is similar to the RX-F801. If you put a bind plug to connect channels 1 and 3, you will get PPM output on channel 4. This plugs into the RC port of the PixRacer. It’s also important to use channel 3 for thrust on the transmitter. The receiver has a built-in failsafe mode, which centers channels 1,2 and 4 and channel 3 to low if the rc link is lost. I started with thrust on channel 1, but if you then switch off the RC and the vehicle is still armed, it will apply half throttle (and pitch backward)!

RX-F802 RC receiver
RX-F802 RC receiver

Firmware

Helicopters are not yet supported by PX4, so I had to do some programming. First I made a new airframe definition in PX4/Firmware/ROMFS/px4fmu_common/init.d

#!nsh
#
# @name Blade 130X
#
# @type Helicopter
#
# @maintainer Bart Slinger <bartslinger@gmail.com>
#

sh /etc/init.d/rc.mc_defaults

set MIXER heli_120deg

To make it visible in QGroundControl, you have to overwrite ~/.config/QGroundControl.org/PX4AirframeFactMetaData.xml with the airframes.xml generated when building PX4 firmware.

I also had to make a new mixer, which was inspired by the CCPM.main.mix mixer. However I found out that the left and right servos had longer arms, so no compensation was needed in the mixer. The ESC is on AUX1, which I control with a turn-knob on my transmitter.

Helicopter 120 degree Cyclic-Collective-Pitch Mixing (CCPM) for PX4FMU
==================================================

Output 0 - Left Servo Mixer
-----------------
Left Servo = Collective (Thurst - 3) - 0.5 * Elevator (Pitch - 1) + 0.866 * Aileron (Roll - 0)

M: 3
O:       10000   10000      0 -10000  10000
S: 0 3   10000   10000      0 -10000  10000
S: 0 1  -10000  -10000      0 -10000  10000
S: 0 0   10000   10000      0 -10000  10000


Output 1 - Front Servo Mixer
----------------

Rear Servo = Collective (Thrust - 3) + Elevator (Pitch - 1)

M: 2
O:      10000  10000      0 -10000  10000
S: 0 3  10000  10000      0 -10000  10000
S: 0 1  10000  10000      0 -10000  10000


Output 2 - Right Servo Mixer
----------------
Right Servo = Collective (Thurst - 3) - 0.5 * Elevator (Pitch - 1) - 0.866 * Aileron (Roll - 0)

M: 3
O:       10000   10000      0 -10000  10000
S: 0 3   10000   10000      0 -10000  10000
S: 0 1  -10000  -10000      0 -10000  10000
S: 0 0  -10000  -10000      0 -10000  10000


Output 3 - Tail Servo Mixer
----------------
Tail Servo = Yaw (control index = 2)

M: 1
O:      10000  10000      0 -10000  10000
S: 0 2  10000  10000      0 -10000  10000


Output 4 - Motor speed mixer
-----------------
This would be the motor speed control output from governor power demand- not sure what index to use here?

M: 1
O:      10000  10000      0 -10000  10000
S: 3 5      0  20000 -10000 -10000  10000

The final change needed is in the control law. I did manage to get it flying with the default multicopter controller, but tuning is very hard. To make it easier, I changed to control law to use a direct feed-forward from attitude-error to actuator deflections. In mc_att_control_main.cpp function MulticopterAttitudeControl::control_attitude_rates:

_att_control = _params.rate_p.emult(rates_err) + _params.rate_d.emult(_rates_prev - rates) / dt + _rates_int +
// _params.rate_ff.emult(_rates_sp - _rates_sp_prev) / dt;
_params.rate_ff.emult(_rates_sp);

The final thing is to use the kalman filter, because otherwise attitude estimation is f*cked up with the autopilot hard-mounted to the frame. The EKF2 can be selected in QGroundControl parameters section. It works surprisingly well given the massive vibrations on this thing.

]]>
https://www.bartslinger.com/px4/pixracer-on-blade-130x-helicopter/feed/ 0
BLHeli RPM Measurement Output https://www.bartslinger.com/diy-projects/blheli-rpm-measurement-output/ https://www.bartslinger.com/diy-projects/blheli-rpm-measurement-output/#respond Thu, 21 Apr 2016 21:08:10 +0000 https://www.bartslinger.com/?p=470 Read More »]]> I needed an RPM measurement from a helicopter motor. The ESC I use is a YEP7A from HobbyKing. Unfortunately it has no free solder-pads. The pads used for programming are also connected to the FETs, probably to save some space on this ultra-small ESC.

Pads for programming, all shared with FETs.
Pads for programming, all shared with FETs.

Pinout from HobbyKing website
Pinout from HobbyKing website.

 

I choose to remove the LED, since it is not used by the BLHeli firmware anyway. The LED is connected on PORT D, PIN 3 (PD3). Before removing the LED, lets first test if we can show the RPM measurement using a blinking LED. The LED is connected to +5V with a pull-up resistor. That means that the LED is ON when the output on PD3 is LOW and vice versa.

YEP7A LED and Pull-up resistor.
YEP7A LED and Pull-up resistor.

 

This worked, so it was time to remove the LED and replace it by a wire. I also removed the pull-up resistor, because then I could use both pads of the LED to solder the wire. This wire goes to the autopilot which processes the pulses from the ESC. A drop of hot-glue was also added to relief the solder joint.

photo_2016-04-21_22-25-18
LED replaced by a wire.

To enable the RPM output, the code has to be modified slightly. The source-code for the BLHeli firmware can be downloaded at Github. For every ESC, there is an include file (*.inc), in my case YEP_7A.inc.

First, we need to configure the LED pin (PORT D, PIN3) as an output. This part of the code was modified to accomplish this.

;*********************
; PORT D definitions *
;********************* 
;.EQU            = 7    ;i
;.EQU            = 6    ;i
;.EQU            = 5    ;i
;.EQU            = 4    ;i
.EQU    LED_Pin  = 3    ;o
.EQU    Rcp_In   = 2    ;i
;.EQU            = 1    ;i
;.EQU            = 0    ;i

.equ    INIT_PD        = 0x00
.equ    DIR_PD         = (1<<LED_Pin)

All the way at the bottom are the macro’s for the RPM measurements. They are empty by default. It is changed as follows:

.MACRO Set_RPM_Out
  sbi  PORTD, LED_Pin
.ENDMACRO
.MACRO Clear_RPM_Out
  cbi  PORTD, LED_Pin
.ENDMACRO

These macro’s will be called from the main code. The sbi and cbi commands are assembly instructions for “set bit” and “clear bit”.

After these modifications, the code needs to be compiled. This is easiest on Windows, since a batch file is provided that does it for you. Simply run MakeHexfiles.bat. The generated HEX and EEP files appear in the Output folder. This script needs the avr assembler. I got it by installing Atmel Studio. In the MakeHexfiles.bat file, I changed the AtmelPath to:

SET AtmelPath="C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avrassembler"

The generated HEX file can be uploaded with BLHeliSuite. The EEP file needs to be in the same directory.

]]>
https://www.bartslinger.com/diy-projects/blheli-rpm-measurement-output/feed/ 0