Saturday, December 19, 2020

Pinout of nRF51822 board HW-651

 I recently bought a nRF51822 board to build a Bluetooth mouse for disabled people. The idea is that if I can get a Bluetooth HID service going on an nRF chip, then I can add various sensors to it and use it for their various needs. It can be a mouse, it can be a key or other things. I opted for the nRF family because they support open source applications and therefore a non-bloated development environment can be produced.

The board that I bought for the purpose is this:

nRF51822 board marked "HW-651". Note nothched corner.

Well, there is a problem. No datasheet came with it nor can be found anywhere on the Internet. The pinout is completely unknown. It is also different from the pinouts of similar boards. So this post is to provide the information that I have found so far. 

There are two pin headers marked "J1" and "J2", each with 2x9 pins. J1 is under the WiFi symbol, and J2 is under the marking "HW-651". Pin 1 has a square pad, and for J1 it is just next to the WiFi symbol and towards the outer edge of the board, and for J2 it is next to the "HW-651" marking, towards the center of the board. The pin numbers proceed in a zig-zag pattern as generally done in pin headers. BTW, the pin header is small and not the .1' type. It appears to be metric with 2mm spacing.

I traced the pinouts from the datasheet and a magnifying glass. The power supply pins and some others, were tested under the microscope with a continuity tester. A few of the pins are yet unidentified. They are marked "?" in the table.


This is J1 pinout:


Func Pin Hdr IC Pin IC Pin Pin Hdr Func
P0.21
1 40
13,33,342GND
P0.23
3 42
41
4 P0.22
P0.25 5 44 43 6 P0.24
? 7 ? ? 8 ?
P0.29 9 48 47 10 P0.28
P0.30 11 3 1,35,36 12 Vdd
P0.00 13 4 13,33,34 14 GND
P0.02 15 6 5 16 P0.01
P0.04 17 8 7 18 P0.03

This is J2 pinout:


Func Pin Hdr IC Pin IC Pin Pin Hdr Func
P0.20 1 28 272P0.19
P0.18 3 26 25 4 P0.17
SWCLK 5 24 23 6 SWDIO
P0.16 7 22 21 8 P015
P0.14 9 20 19 10 P0.13
P0.12 11 18 17 12 P0.11
P0.10 13 16 15 14 P0.09
P0.08 15 14 11 16 P0.07
P0.06 17 10 9 18 P0.05

I will determine the missing pins under a microscope with continuity tester when I have more time. I have checked the pins VDD, GND, SWCLK, SWDIO to be correct (I got a reply from the chip with the progrmmer.)

Sunday, September 27, 2020

Asus T200 Transformer Does not Power Up

 

A friend handed over a "dead" Asus T200 Transformer tablet/notebook recently. He said: "It is broken. Use its components if you want and throw away the remains!" It looked in good condition, so I checked the Internet about possible remedies. Many tablets can be restarted from power off or sleep state by using the "Power" and "Volume Up or Down" button press. I tried it, and hey, the computer powered up. So easy (but I would not be writing it if it were so easy; read on!).

Asus T200 - many have power up problems.


 

 Anyway, after the usual bragging about how sensitive my hands were etc, and saving his files, after a few days, surely it started to power up with more and more difficulty! Rituals of power button combinations, adapter connected/disconnected and all sorts of stuff. Eventually it was dead as a stone. As a last resort, I opened it up, disconnected the battery for a while and re-connected. However it did not help. Dead as a stone...

 Then I started looking around the circuitry with a magnifying glass. After a while I noticed a telltale sign of corrosion around the connector of the power daughter PCB (the power and volume buttons are here). For those who are not familiar, corrosion looks like tiny green residue around gold plated contacts. Removing the connector, sure enough, there was corrosion in one line of the ribbon cable. The connector is outlined in the photo below. All that was required was to remove the cable by prying the black tab on the connector up, cleaning with alcohol, and re-inserting. The computer has been working fine since then.

Check for corrosion on the power daughterboard connector and cable.

There is much discussion about how to solve dead T200's through software, but this looks like a very real and repairable hardware fault. How to open your T200 etc. can be found on the internet and I wont repeat it here. Just remember to charge it before it can power up!

So, although the T200 is now an old computer, it is still capable of daily tasks like teleconference and web surfing. If you have one lying around, it may be salvaged.



Thursday, May 7, 2020

USB Data Acquisition on a $3 Board


It is possible to build a simple data acquisition system using the STM32F103 processor. A few analog ports are sampled at the frequency defined by the user and the result is sent over USB configured as a virtual com port, in human readable format to a computer. The code is available at my GitHub repo. This project demonstrates a few things:

  • Sample signals with a fairly precise sampling time,
  • Use ADC, DMA and a timer for analog to digiral conversion in hardware, completely independent from software,
  • How to use the CubeMX graphical configuration and code generation tool,
  • Create a USB CDC device (virtual com port, actually a virtual modem device),
  • Show what can be done using only a $3 microprocessor board.

 
Pots used to generate the voltage data.


The system uses a timer to trigger the ADC, which automatically samples one sequence of the channels you specify, stores them using DMA into your array. It finally sets a flag in an interrupt service routine (ISR).


At the same time, a USB CDC device is also set-up, so this appears to the computer as a virtual serial comm port (actually a modem device). At the firmware side, all that the user needs to do is to check a flag, called the ADC sequence complete flag, and if it is true, get the ADC results, format them into a string, and send the string to the host computer over the comm port. Since a standard USB device class is used, it should work on most modern OS'es without a driver.


The project also demonstrates how to use STM32 CubeMX to create a fairly complex system using the graphical user interface (GUI). The USB device setup can be simply created, as well as the timer, ADC, DMA, the links between them and the interrupts that they generate. After this, we only need to fill up a few locations, typically in main.c. Since STM32 CubeMX auto-generates the project sources, you must not interfere with its code. It must not interfere with what you write either. To deal with this, ST has come up with a method, where they seperated the generated code with this type of comments:

/* USER CODE BEGIN Includes */
  -> your code goes between these comments and survives CubeMX source re-generation.
/* USER CODE END Includes */
 -> whatever you write outside gets erased the next time CubeMX
re-generates your project source.

If you write between them, STM32 CubeMX does not touch your parts of the code even if you re-generate it using the GUI. Well, the method does make for bloated code. But at the end, if you are sure that you will not go through another re-generation iteration, you can delete all those markers and you will be fine and your code senile and readable (ask the OCD in me!).


The code is built as a GCC project using GNU make. The Makefile is modified a bit from the original, because the autogenerated one has too much repetition in file paths etc. I also added a "make jflash" rule so that you can use Texane's excellent st-util for ST-Link V2 programmer. To compile, you should have a GNU-ARM-Embedded toolchain installed (such as from the Launchpad site). Edit Makefile to point to your toolchain path. (BTW, I intentionally obfuscated the Makefile after it was first generated by CubeMX. In later re-generations of the source, CubeMX fails to recognize your Makefile for updating, does not modify it, and gives an error message. This is intentional. Just ignore that error).


After you program the processor, power it down and plug it back in through the USB port. It should appear as a CDC device (such as /dev/ttyACM0). Connect to it using a terminal emulator (I like kermit). After you set the port (no need to set connection speed in ACM devices) and connect, you should see the readings coming from ADC1, 2, 3 as:

...
200: ADC1=0, ADC2=4029, ADC3=4030
201: ADC1=1039, ADC2=4029, ADC3=4029
202: ADC1=2353, ADC2=4030, ADC3=4030
203: ADC1=3503, ADC2=4030, ADC3=4030
...
 
The first number is sample sequence. How you format the data is up to you. The above format is just for clarity; in a normal application a comma separated list is probably better. You can send data in binary form also. You can even build a simple data graph of sorts by using the excellent SerialPlot program. Give it a try!

There are a few things that can be changed:

  • Add or remove ADC channels -> See the ADC section in CubeMX
  • Change the sample and hold duration -> See the ADC section in CubeMX
  • Change sampling time -> See TIM3 section in CubeMX. The sampling time is calculated as follows: The processor runs at 72MHz. This is first divided in the prescaler (it is currently set to 7200), and further in the ARR (max count limit of timer/counter) which is set to 1000. The overall sampling time (Ts) is therefore 72,000,000  /7200 /1000  = 10 samples/s => One sample at every 0.1s, or, Ts=100ms. Or course, it can be much faster than this; this rate is only to make it easy fo visually follow the output.
You can modify them either in the source code or in CubeMX. If you modify directly in the source code, your changes will disappear the next time you re-generate the source from CubeMX. So it is better to do it from CubeMX.


Going through the source:


Here are some main points about the source code. The important thing to remember is that CubeMXis a tool for initializing the hardware. It does not generate a program that is useful for anything. We need to write that. Let's go over the significant parts. (Some of the parts mentioned below are already filled in by CubeMX):

It is important to start with the includes for the USB CDC functions. We add:

 #include "usbd_cdc_if.h"

This is the array for the string sent over the virtual comm port (we add):

   char msgbuf[128];

From this line onwards is the meat of the matter:


   /* USER CODE BEGIN 2 */
 
The following shows how to send a string over the virtual comm port:
 
  sprintf(msgbuf, "Starting device.\r\n");
  CDC_Transmit_FS((uint8_t *) msgbuf, strlen(msgbuf));


Starting the timer and the DMA are user's responsibility:

  HAL_TIM_Base_Start(&htim3); //AO!: Start the timer.

The ADC and DMA are initialized with the following HAL function. It specifies which ADC to use, which array the results of the conversion are stored and the length of the transfer. The actual link between the ADC and DMA was set in stm32f1xx_hal_msp.c:
 

  HAL_ADC_Start_DMA(&hadc1, adcBuf, ADC_BUFLEN); //Link DMA to ADC1

The infinite loop follows. It simply waits for the ADC_FLAG to become TRUE, which is set by the DMA transfer complete callback function. When this happens, the ADC results have already been stored in adcBuf[ ] array. We format  them as a string and send over the virtual comm port:

      sprintf(msgbuf, "%d,%d,%d,%d\r\n", i,
          (int)adcBuf[0], (int)adcBuf[1], (int)adcBuf[2]);
      CDC_Transmit_FS((uint8_t *) msgbuf, strlen(msgbuf));


CubeMX links ADC and TIM3 as:

static void MX_ADC1_Init(void){


The next significant part is the modification of the DMA transfer complete callback function, which sets ADC_FLAG. We add:


/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1){
  ADC_FLAG=TRUE;
}


This is essentially all the important changes to main.c. There is one extra. I modified the error handler so that it sends the file and line number of where the error function was called, and then blocks there while flashing the LED at 20Hz. If the virtual comm port ever came up correctly by the time an error occurred in your program, you should see where the error was generated. This is done in _Error_Handler function.

The next significant file is stm32f1xx_hal_msp.c where the link between TIM3, ADC and DMA are set around the line /* ADC1 DMA Init */ (already done by CubeMX):

 __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);


So that's it. Please modify the code to suit your needs. "Share and Enjoy!"


PS: If you are new to STM32 programming in GCC, please see my blog on how to set up a development environment for it.

Wednesday, May 6, 2020

Flysky i6 - Suddenly broke down!


My previous blog was about building a flight sim controller using a regular Flysky i6 transmitter. It worked nicely for some time, until one day, the screen suddenly went blank, and it was completely dead...

I checked the regular suspects. The voltage regulators for the processor and the RF module worked fine and everything looked OK. It just would not turn on at all. I was thinking that probably I shorted the 5V coming from the USB port to the 3V3 line on the board for an instant and burned some of the main chips. I gave up on the device and started to build my custom joystic interface electronics and new firmware that read i6's potentiometers as the input to the joystick interface. I was planning to bypass all the electronics of the i6, and use only its sticks (analog voltages of the pots are nicely broken out at the top, just below "CON8").

The day to start wiring it up to the joystick interface. I switched the i6 on out of usual habit. It turned on normally and worked! WTF! Alright, it means intermittent connections. However, it worked only for a short time and went bad again. Such intermittent operation continued from then on. Usual suspect in this case, is bad connections (solder joints, connectors, wires etc.). Looking at the board under a makeshift microscope, the EEPROM chip seemed to have suspicious soldering. I resoldered it. No difference. Then I noticed the top cover of the crystal was bent inwards, as if struck by something. I did not have a 4 pin 5032 SMD package 8MHz crystal laying around, so I unsoldered it and replaced it with a through hole regular crystal. It did work a bit better but still went bad from time to time. When it did work, pressing on the processor package would kill it. But looking at the processor solering, all looked fine.

Eventually, I laid my eyes on the large RF module. I had not given it much thought until then, because in regular RC transmitters, the RF module simply receives input signals from the rest of the circuit. Even if it is broken, the rest of the system would still work. But this one is different; it has bi-directional communication with the processor for setup and running. If it does not cooperate, the processor cannot go further in the program. It had one of the worst soldering job that I have seen on Chinese products (too large for the pick and place machine, it was probably hand soldered). Fairly wide pin spacing, so it was easy to re-solder it. Well, that was the problem. After the re-soldering, it has worked flawlessly since.

The bad solder joints on the RF module...
In post-mortem, I understood why it broke. I had removed the PCB from the transmitter to check some of the soldering side traces to the trainer port. However, the PCB is held very tightly in plastic standoffs and requires much  force to get it out. The RF module is fairly large and there is no strain relief betwen it and the PCB. So all three conditions must have come together and probably caused some solder connections or their PCB traces to crack. The processor then received no reply from the RF module at startup, and the whole system simply hang.

Now, some rant. There is obviously a reason why these units are so cheap compared to brand name products. It is inevitable that factory reject parts or shady components find their way into them. The plastic or wiring is not the best quality. In this case, the crystal was obviously of dubious quality. I still think that Flysky i6 is exceptionally well made, and if you don't fiddle with it, it will give years of good service. I also think that it is a great platform to tinker with, especially if you do  not use it for a critical system, such as a flight simulator.

So there it is. If your Flysky i6 breaks down and goes completely blank at some point, the RF module re-solder is worthy of a repair candidate.

Saturday, April 4, 2020

Fly Sky i6 as a flight simulator TX

These days I cannot fly physical RC, so I thought about flying with a simulator. There are very nice simulator software out there. One of the best is Picasim. It has great flight dynamics and graphics. More importantly for me, it can easily run under Linux using wine (give it a try!). Next is the transmitter box. While it is possible to buy a transmitter box for flight sims, they are generally not good quality or offer much configuration.

Looking around the web, I found Fly Sky i6, an entry level transmitter which offers a great number of options. Better still, it is available from China without a receiver or any other accessories, for about $35 including shipment. This is about the same price as "dumb simulator transmitters". The i6 is very interestig because it has a great followers group who are modifying its firmware to make it better. I would have hesitated to use it for a real flying RC model, but when I opened it up for the first time, I was surprised with the good quality. It can be trusted upon to fly simple real RC models.

Back to the topic. One of the most interesting features of the i6 is its "trainer port". It is documented well. The trainer port is a "female mini DIN 4" shape, A.K.A. "S video", and has 5 connections available: GND, PPM OUT, PPM IN, RX, TX. A matching mini DIN 4 male connector looks like this FYI:
Mini DIN 4 male connector (A.K.A. S video)
Mini DIN 4 male connector (A.K.A. S video)

If you build (see below) or buy a PPM to USB joystick converter module (for around $11), you can immediately use the transmitter straight away. However, there are two "problems" with that! First of all, where is the fun of building if you are simply buying stuff and plugging them together? Secondly, of you buy a sim module, then you will need to run the transmitter on batteries which you must remember to buy replacements or to charge. The purpose of this post is to describe the modifications to the i6 electronics so that you can power it directly from your USB port.

Two modifications are required to the i6:
  1. Modify the trainer port pins so that one of them becomes a power input pin,
  2. Reduce the power consumption so that it will run from the 100mA available from the USB port.

Modification of the tranier port functions

You need to eliminate one of the functions on the trainer port so that you can make room for the power input pin. I selected to eliminate the "PPM IN" pin (of course another can be selected). In essence, all that you need to do is cut the wire to the PPM IN pin from the connector and solder it to the power switch and you would be done for this step.
However, there is a smarter way! The trainer port is connected to the rear case with a cable and a nice connector. It is possible to make a few changes to the components so that the cable is intact. The changes are shown below:
Changes to the PPM IN connection to make it a power supply pin.
I labeled the changes in the photo to match the item numbers below.
  1.  The PPM IN signal goes to connector J8, second pin from top. (The top pin is GND by the way, and the third from top is PPM OUT pin.)
  2. There is a reverse biased protection diode D1 that shorts the PPM IN to 3V3 supply on the board if it exceeds the allowable voltage. Remove that diode. Connect a wire to its left pad (next to the buzzer). This is a bit fiddly because of lack of room and you can see some burn marks on the buzzer. Well, it still "buzzes"...
  3. Connect the other end of the wire to the battery (+) pin. There is actually a more convenient pad for that labeled "BAT+" just to the left of the battery connector.
  4. If you find it difficult to solder to an SMD diode pad, there is also another easy location for the PPM IN pin here.
  5. Finally, you will need to remove the in-line protection resistor R13 that used to connect PPM IN to the processor. I generally lift up one pin and still leave the resistor connected, so that I can take back the changes in the future. In case you need, this is a 1k Ohm resistor.

That is all. Now the "PPM IN" pin on the trainer socket is connected to "battery in" circuit. CAUTION! This has two implications: The USB 5V is now connected directly to the battery box output. If you have batteries in the case, their voltage will be applied to the USB socket of your computer. Always remove batteries from the transmitter before using it. Second; because the USB supply voltage is only 5V, there is no room to use an in-line voltage protection. The i6 starts low voltage alarm when the supply voltage falls to around 4.5V. If you apply a high voltage to this pin (12V or similar) you will burn out the electronics. However, a simple reverse protection is installed, in the form of reverse biased diode D2 across the power supply. I find it good to have batteries installed for a balanced feel. So you may want to install only 3 of the 4 cells, or keep the power connector of the battery box unplugged, or cover one of the cells with tape etc. to keep them all installed. I selected the last option.

Reduction of power consumption

The second item is to reduce the power consumption. The transmitter draws about 100mA with the backlight on. The USB joystick module also requires about 20mA. The total of 120mA is above USB power supply specification and may cause a reset of the USB port on some PCs. The simplest way to reduce power is to remove power to the transmitter module of the i6. However, the microprocessor must communicate with it, and probably will not run without it. If there were a firmware upgrade that disabled the transmitter module, it would have been the best, but I did not come across one. The next biggest load of the i6 is the backlight. It consumes about 20mA and it is simple to disconnect. So that is what I did.
 
All you need to do is cut one trace so that the backlight is disabled. You can also remove R13 but i my case, it was firmly glued down. Anyway, if you want to re-enable the backlight, simply bridge the cut. BTW, the backlight is supplied directly from battery voltage.
 
Cut this trace to disable the backlight.

So, that is all. Now the i6 can be powered directly from USB, and the PPM IN pin has become a power input pin. If needed, it is still useable as a regular transmitter.

FWIW, my Fly Sky i6 PCB is labeled i6-M VER 2.4  20190623


The interface

Finally, the simulator interface (A.K.A the cable) itself. This converts the PPM (Pulse Position Modulation) signal which is standard across most RC transmitters, through the USB interface, into a joystick emulator for the PC. If you look around, there are many candidate projects. If you have an Arduino Leonardo or similar laying around, you can search for such projects. Since I am currently doing development with STM32 series microcontrollers, I had a blue pill board (STM32F103 breakout) laying around and chose to use that. Again, there are many good projects for it especially in Github. I found this one: "STM32-RC-USB-Adapter" by "voroshkov" and did not even bother re-compiling it; used the binary straight from the repo. It worked like a charm. It only supports 4 analog channels. I may in the future upgrade it to 6 all channels if I have time. I have modified the firmware to a contemporary version of MX Cube and added the 5th channel. I will put it up to GitHub later and put a link here. However, there is really no use for an extra analog channel in the simulator. 

The connections to the Blue Pill Board -> i6 are simple. Just buy a mini DIN 4 pin male plug and use that:
  1. GND -> GND
  2. 5V     -> The newly created Power input pin
  3. A8     -> PPM OUT
The photo is below:
The completed PPM to USB joystick adapter

Finally, you need an adjustment. Gamer joysticks have poor gimbals with a lot of electrical noise, so that most OS'es set the default dead zone at the center to a large value and you will not have precise flight control. In your OS remember to calibrate the joystick for good performance. Typically the OS will require that you move the sticks to extremes and then center them while it tries to detect the amount of noise. There is an easier way to calibrate an i6: Manually set each joystick minimum to '0', maximum to '1000', dead zone min and max both to '500' for the best performance.

The setup works well with my 2016 vintage Linux laptop running Ubuntu 16.04 as well as a contemporary Windows 10 machine. There is really no difference to the USB joystick definition across OS'es.

Have fun!