Sunday, October 25, 2020

What is the Disk System and What isn't? #3: Boot Sector


Hi there. In the previous two articles of this series, I discussed the physical structure of the disks and how they are partitioned. In this article, I will handle a more specific subject instead of a generic one. There is no boot sector in linux as we know it from DOS. NTFS on Windows is complex enough to be a topic on its own. Therefore, in this (and next) article, I will concentrate on DOS. The topic, which I will be handling in this article, will be the structure discussed in the Volume Boot Record (VBR) article on Wikipedia. At the end of this article, I explained why I chose to call this structure as "boot sector". Now, what is this boot sector really?

Yes, What is This Boot Sector?
Boot sector is the first sector of any partition with FAT and NTFS (and its predecessor HPFS) file systems (FS). This structure is not available on FSs such as ext and XFS. Its structure contains a block of code and some disk related information. Its function is similar to MBR, such that, the MBR loads the boot sector and executes it, likewise boot sector needs to continue the boot process and load the DOS kernel (IO.SYS) or the WinNT pre-kernel (NTLDR) and then NTOSKRNL.EXE. By the way, as of Windows Vista, there is /boot/bcd file instead of NTLDR [1].

Floppy disks and some USB disks cannot be partitioned because they don't have an MBR. In contrary, hard disks can be partitioned thanks to their MBR. From my previous article, we already know what BIOS and MBR code do. I also wrote repeatedly that there is no difference between MBR and boot sector from BIOS point of view (Citation: Code in the MBR and VBR is in essence loaded the same way. [2]). While the system is booting from the hard drive:
  1. BIOS reads and runs the MBR from the first sector. 
  2. MBR code reads and executes the first sector of the bootable partition.
  3. Boot sector code finds and loads IO.SYS and the boot process continues.
Second step is skipped when DOS is booting from the floppy disk (or any non-partitioned media) because the first sector of a floppy is the boot sector. And the boot sector code is loaded to 0:7C00h like MBR code.

Note: UEFI provides another method for loading OS [3]. The steps above are correct in environments without UEFI.


What is in the Boot Sector?
In the boot sector, there are data about the disk structure and the file system in that partition. Using these data, location of the (kernel) files is calculated which is required for the boot process. These data are primarily information about clusters, which are the logical unit of the file system, CHS/LBA info, disk label shown in the dir output in DOS and file allocation table (FAT) version. I will first copy the boot sectors of the systems, I installed, to files. Then, I will examine these according to the boot sector format. 

As I explained at the end of this article (under the "Difference between Boot Sector and VBR"), the terms I use for the concepts are slightly different from those on Wikipedia. The data, I mentioned above, are explained in the BIOS Parameter Block (BPB) and Design of the FAT file system articles in Wikipedia, apart from Volume Boot Record article. My personal opinion is that the term "BIOS parameter block" can be get mixed up with the term "BIOS Data Area (BDA)".


Reading the Boot Sector
I used HxD again to read the boot sector in Windows. Like I did it previously, I run HxD as administrator and opened MBR*. There were three partitions on my computers disk. I am interested in the last one with Windows installed (can be seen in the screenshot from the previous article). I can find out at which sector this partition is starting, using the information from the previous article. I wrote the UInt32 value at 1E6h offset, which is the starting address of the third partition, into the sector box above and brought up the boot sector. I am only doing this to show how much NTFS boot sector looks like FAT.

* To find partitions on GPT disks, it is necessary to read GUID entries. I will explain GPT format and how to read it later in separate article.


Red marked region is code, blue is data and green is the boot sector signature.

I had created and formatted two primary partitions on the DOS622 VM. I boot this machine with Damn Small Linux (DSL) and open the terminal. There is no need to install any program to read the boot sector in linux. I see two disks: /dev/hda1 and /dev/hda2, when I check them with sudo fdisk -l command. I can either read the disk with dd and redirect the output to hexdump, or I can read the disk with hexdump and look at the first 32 lines (512 bytes) using head command:

sudo hexdump -C -v /dev/hda1 | head -n 32

or

sudo dd if=/dev/hda1 count=1 bs=512 | hexdump -C -v

Same commands can be also applied to /dev/hda2. I actually do not need to copy these files to my machine but they can be easily copied with scp just in case, as explained in the previous article.

In FreeDOS VM, I had created one primary and one extended partition and two virtual partitions within the extended partition which I did not format. When I booted this VM with DSL and check the partitions with sudo fdisk -l, I see primary partition /dev/hda1 and extended partition /dev/hda2.

Disk /dev/hda: 1073 MB, 1073741824 bytes
64 heads, 63 sectors/track, 520 cylinders
Units = cylinders of 4032 * 512 = 2064384 bytes

   Device Boot    Start       End    Blocks   Id  System
/dev/hda1   *         1        173     348736+   6  FAT16
/dev/hda2           174        520     699552    5  Extended
/dev/hda5           174        346     348736+   6  FAT16
/dev/hda6           347        520     350752+   6  FAT16


Considering their start and end sectors, /dev/hda5 and /dev/hda6 are virtual partitions in the extended partition. When I run the above commands on /dev/hda2, I see the sector that I already saw like in the previous article using the disk editor, which does have a partition table but no code. Sectors filled with 0F6h byte can be seen in /dev/hda5 and /dev/hda6 because I hadn't formatted these disks. This means that the boot sector is written only when the partition is formatted, according to the FS, it is formatted in.

But why 0F6h? This value is designed to be written on empty space when formatting Atari disks. Then IBM added it to the PC standard as it is and it has not been changed since then. As far as I know, there is no particular reason.

Finally, I will check the boot sector of a floppy disk. Therefore, I will create a 1.44MB floppy image file using dd on my linux host:

dd if=/dev/zero of=floppy.ima bs=512 count=2880

The number 2880 is the total number of sectors on a floppy disk as a product of 80 cylinders, 18 sectors and 2 heads. On Windows, a text file of this size can be created and then renamed to floppy.ima. The important thing is that the file size has to be 1 474 560 bytes.

After creating the file, I start DOS622 VM and insert the virtual floppy, I created, to the virtual drive from Settings -> Storage -> Floppy Controller. If I change to drive A: right now, I will get a General failure reading drive A error. (Please note: I came across a bug of format command in FreeDOS. Floppy disk needs to be formatted twice in FreeDOS. Changing to A: after first format, causes an error.)

format A: /S

I formatted the floppy disk with /S parameter as a system disk. So, system files (i.e. IO.SYS, MSDOS.SYS) were copied to the floppy. To look inside the floppy disk, simply open floppy.ima with hexdump. In Windows, HxD must be used, though.


Data Structures of Boot Sector
Of the structures, which are explained in BIOS parameter block article, almost only the NTFS structure is in use nowadays. There is DOS 4.0 EBPB on the VM disks and on the floppy, we just created. Let's take a look at DOS 4.0 EBPB first:

Sector OffsetSize
Description
0x003 byteJMP to the code
0x038 byteOEM NameNote1
0x0BwordBytes per sector
0x0DbyteSectors per cluster
0x0EwordReserved sectors
0x10byteNumber of FATs
0x11wordMax. num. of entries in root dirNote2
0x13wordTotal number of sectorsNote3
0x15byteMedia descriptorNote4
0x16wordSectors per FAT
0x18wordSectors per track
0x1AwordNumber of heads
0x1CdwordNumber of hidden sectors
0x20dwordTotal number of sectorsNote5
0x24bytePhysical drive numberNote6
0x25byteReservedNote7
0x26byteExtended signature (0x28 or 0x29)
0x27dwordVolume serial number
0x2B11 byteVolume label
0x368 byteFS Type
Compiled from Wikipedia. (1, 2)

Note1: OEM name field is a signature of the system formatting the disk. Normally it has no special value. However, arbitrary values might cause some version of DOS not to read the disk.
Note2: (pre FAT32) Due to the FAT FS limitations, number of entries (files + directories) under the directory "\" cannot exceed a specific limit. Root directory is special by design, it has to be in the second cluster and be fixed in size. Of course, this limitation isn't applied to sub-directories.
Note3: Since this field is 16-bit, it is deprecated for the partitions with more than 65 535 sectors. The new field at offset 0x20 takes its place. Its value is usually zero.
Note4: This byte is used to determine the type of the media. Its value is 0xF0 for hard disks and 0xF8 for 1.44M floppies. I do not think that other values will be encountered in practice.
Note5: When the field at offset 0x13 was deprecated (see Note3), this field has taken over its place. If this value is zero, too, besides 0x13, OS finds the total number of sectors from MBR.
Note6: 0x00, 0x01, ... for floppies and 0x80, 0x81, ... for hard disks.
Note7: Even though this space is reserved in documents, chkdsk command of WinNT uses this, as a flag for disk errors.

Let's examine the boot sector of a floppy disk using this information:

00000000  eb 3c 90 4d 53 44 4f 53  35 2e 30 00 02 01 01 00  |.<.MSDOS5.0.....|
00000010  02 e0 00 40 0b f0 09 00  12 00 02 00 00 00 00 00  |...@............|
00000020  00 00 00 00 00 00 29 05  14 76 1b 4e 4f 20 4e 41  |......)..v.NO NA|
00000030  4d 45 20 20 20 20 46 41  54 31 32 20 20 20 fa 33  |ME    FAT12   .3|


First three bytes contains "JMP +3Ch; NOP" commands. Since JMP itself is two bytes, the beginning of the code is at the offset 3Eh (=2+3Ch). Bytes at 3Eh (0FAh, 33h, 0C0h) is CLI; XOR AX, AX (0C0h is not shown above). Other values are:

OEM Name: MSDOS5.0
Bytes per sector: 0200h = 512 byte
Sectors per cluster: 1
Reserved sectors: 1 (the boot sector itself)
Number of FATs: 2
Max. num. of entries in root dir: 0E0h = 224
Total num. of sectors1: 0B40h = 2880
Media Descriptor: 0F0h (1.44M Floppy)
Sectors per FAT: 9
Sectors per track: 12h = 18
Number of heads: 2
Hidden sectors: 0
Total num. of sectors2: 0
Physical drive number: 0
Reserved: 0
Extended boot signature: 29h
Volume serial number: 05h, 14h, 76h, 1Bh = 1B76-1405 (same as the value in the image above)
Volume Label: NO NAME
FS Type: FAT12

Likewise, let's examine the hard disk of DOS622:

00000000  eb 3c 90 4d 53 44 4f 53  35 2e 30 00 02 04 01 00  |.<.MSDOS5.0.....|
00000010  02 00 02 00 00 f8 fa 00  3f 00 10 00 3f 00 00 00  |........?...?...|
00000020  e1 e7 03 00 80 00 29 12  b6 0b 4f 4d 53 2d 44 4f  |......)...OMS-DO|
00000030  53 5f 36 20 20 20 46 41  54 31 36 20 20 20 fa 33  |S_6   FAT16   .3|


OEM Name: MSDOS5.0
Bytes per sector: 0200h = 512 byte
Sectors per cluster: 4 (so each cluster is 2048 bytes)
Reserved sectors: 1 (the boot sector itself)
Number of FATs: 2
Max. num. of entries in root dir: 0200h =512
Total num. of sectors1: 0
Media Descriptor: 0F8h (hard disk)
Sectors per FAT: 250
Sectors per track: 3Fh = 63
Number of heads: 10h = 16
Hidden sectors: 3Fh = 63
Total num. of sectors2: 03 E7E1h = 255 969
Physical drive number: 80h
Reserved: 0
Extended boot signature: 29h
Volume serial number: 12h, 0B6h, 0Bh, 4Fh = 4F0B-B612
Volume Label: MS-DOS_6
FS Type: FAT16


And the structure of FAT32, which is referred as DOS 7.1 EBPB on Wikipedia. First 36 bytes of the structures are same:

Sector OffsetSizeDescription
0x0036 byte(see FAT16 structure)
0x24dwordSectors per FAT
0x28wordMirroring flagsNote8
0x2AwordVersion
0x2CdwordRoot directory clusterNote9
0x30wordFSINFO sectorNote10
0x32wordBackup boot sectorNote11
0x3412 byteReservedNote12
0x40bytePhysical drive numberNote6
0x41byteReservedNote7
0x42byteExtended signature (0x28 or 0x29)
0x43dwordVolume serial number
0x4711 byteVolume label
0x528 byteFS type
Compiled from Wikipedia. (1, 2)

Note8: Normally, file allocation table is kept as at least two copies. Each file read or write operation is done using both copies. With this flag, single FAT can be set as active.
Note9: This field is added in the new design, in order to remove the design limitations mentioned in Note2.
Note10: With FAT32, theoretical maximum number of clusters increased from 65526 to 228=268 435 456. Correct, 228, not 232 because 4 bits are reserved. Even in this case, there are too many clusters to examine, in order to calculate free space in FS or find an empty cluster. This causes performance issues. To overcome this, a separate data structure, called FSINFO, is defined to keep information about free space and clusters. This structure is usually kept in first sector, right after the boot sector and it has RRaA signature.
Note11: With FAT32, boot sector is backed up against any damage. This backup is always written to the sixth sector. If MBR code cannot read the zeroth sector, it tries to read the sixth sector. Although this behavior is hard-coded, a field is also defined in the primary boot sector, to point backup boot sector however it is not recommended to have a value other than six.
Not12: This field originally appears "boot file name" in the documentation. Normally, the name of the kernel file, that boot sector will load, is hard-coded in the boot code. Looks like this field is intended to keep the file name here.

I compiled some of the above notes from Microsoft's FAT32 File System Specification document. Unfortunately, this document is no longer available on MS website, but it can be downloaded here.

Finally, NTFS partitions have a very similar structure to FAT32. Like FAT32, first 36 bytes of this structure are almost the same as FAT16.

Sector OffsetSizeDescription
0x0036 byte(see FAT16 structure)
0x24bytePhysical drive numberNote6
0x25byteReserved / Unused
0x26byteExtended signature (0x80)
0x27byteReserved
0x28qwordSectors in volume
0x30qwordCluster number of Master
File Table (MFT)
0x38qwordCluster number of MFT mirror
0x40dwordClusters per File Record
Segment (FRS)Note13
0x44dwordClusters per Index BlockNote13
0x48qwordVolume serial number
0x50dwordChecksum
Compiled from Wikipedia and microsoft.com (1, 2, 3)

Note13: Two fields at the offsets 0x40 and 0x44 are actually defined as 1 byte + 3 bytes, not dwords. Three bytes reserved fields may have been intentionally defined for compatibility because these offsets has a different usage in FAT32. More precisely, to prevent a FAT32 disk utility from accidental corruption of an NTFS partition.

As I mentioned before, NTFS boot sector is similar to its predecessor, FAT but more complex. Therefore, I will explain NTFS in a separate article in order to keep this one short.

In MBR article, I mentioned two different terms about disk size. One of them was raw disk space and the other was formattable (usable) disk space. There are two fields in the tables above, i.e. reserved sectors and hidden sectors. Although they are used in some calculations, they can also be used to allocate some hidden space between root directory and boot sector, in theory. Other than that, there are two FATs that keep the location of the files on the partition. Without these structures, there is no FS at all. In other words, files cannot be written or even if they are written directly to the sectors, their beginning and end cannot be known. While raw space is the space that comes out when the number of sectors in MBR is multiplied by 512 bytes (bytes per sector), formattable space is remaining space on the disk when the above mentioned structures are taken out from the raw space.

For example, I had created a floppy image file of 1 474 560 bytes (2880 sectors). This is its raw space: I can write so many bytes on a floppy without any file structure. What does DOS say about the formatted floppy in the screenshot below?


It says, 2847 out of 2880 sectors can be used. Two FATs each of them 16 sectors plus one boot sector makes 33 sectors in total. This means, I cannot create a file larger than 1 457 664 bytes on this floppy disk.

This claim can be tested by booting with DSL and running following commands:

sudo mount /dev/fd0 /mnt
sudo rm /mnt/*
sudo dd if=/dev/zero of=/mnt/test
sudo ls -la /mnt

Remember, FreeDOS VM has three partitions. Two of them are extended and one is primary. DOS622 VM has two primary partitions and the half of the disk has no partition. Now I will demonstrate an easy way for all that is mentioned above but unfortunately only for those who have Norton Disk Editor.

I ran diskedit.exe (v2000) on the DOS622 VM and opened the partition table of the disk with Alt+A (see the next figure upper part). Afterwards, I pressed Enter again (cursor was on the first partition) and the boot sector appeared with all information read (bottom part). Moreover, the values reported by DOS, are shown in the right column. What could this be used for? Let's assume, I connect a bad hard disk or insert a bad floppy disk (which is not physically bad, of course), to my computer which is running DOS. I check the disk with the disk editor and find out, "Sectors per cluster" value in the boot sector is different from what the OS reports. By replacing this value with its correct value, I can possibly make the disk readable again - even though with a small possibility. On this screen, I can note the partition label and partition serial number down and verify them with the output of dir command after exiting the disk editor.

It is possible to open the MBR again, using Alt + A key combination in the disk editor (please note the "Absolute Sector 0" on the bottom right) and check the other partition (or partitions of FreeDOS) in the same way. Since there is no big difference between other partitions I will not examine them separately.


How Does the Boot Code Work?

a. FreeDOS Boot Code
In this section, I will examine boot code of FreeDOS and give an overview of DOS boot code as well. First of all, I should emphasize that FreeDOS boot code differs slightly on floppies (FAT12) and hard disks (mostly FAT16). In the text, I will mention the difference in the related paragraph.

When a floppy disk is formatted in FreeDOS without /S parameter, the boot code terminates the boot process abruptly with a simple error message. In other words, no boot code is written. This might be most likely because FreeDOS boot code is more complex than DOS, there may be no space for error messages like "Missing IO.SYS etc.". This code is so simple that it's not worth to dwell on it.
The best thing of FreeDOS is that it is open source. I downloaded FreeDOS boot code from github and included my own comments on some lines. These start with "; --" to distinguish easily. The original file can be downloaded from this link and the file containing my comments can be downloaded here. What this code does is not very complicated. First, a jmp command (line** 69) branches into the real code block. Right after that, there are definitions of the boot sector data structures (lines 73-90). I mentioned that this code is loaded at the address 0:7C00h. The code copies itself to address 1FE0h:0 (lines 162-168) to prevent the kernel to be loaded to address 0060h:0 from overwriting itself and continues to run from where the execution left by executing a far jmp command. Once the kernel is loaded, kernel.sys may need some data from the boot sector even if the boot code has finished running.

**: The given line numbers are with respect to the file, in which I added my comments.

There are three important values in the boot code:
1. FAT start (fat_start): The sector, from which the FAT starts. It's the sum of reserved sector and total number of sectors.

fat_start = hidden_sectors + reserved_sectors

I mentioned above that reserved sectors can be used to allocate space between boot sector and FAT. This field gives how many sectors FAT is after boot sector. For FAT12 and FAT16, this field is almost always 1, meaning FAT starts right after the boot sector. The term "hidden sectors" is kinda misleading. This field actually keeps on which sector that boot sector is. In other words, it is the LBA address of that sector. This field is only taken into account when booting. For a non-bootable disk, this field can be zero without problem.

2. Start of root directory (root_dir_start): Where the list of files in the root directory start. It is obtained by adding the product of number of FATs and sectors per FAT to the FAT start value from the first step.

root_dir_start = fat_start + number_of_FATs * sectors_per_FAT

3.First data sector (data_start): This is found by dividing the max. number of root directory entries to the 1/32 of the bytes per sector value and adding start of root directory value (from above step) to this. Since a file entry is 32 bytes in size, bytes per sector divided by 32 gives the number of file entries in a sector. And maximum number of root directory entries divided by this number gives the exact length of root directory table in sectors.

data_start = root_dir_start + root_dir_entries / (bytes_per_sector >> 5)

Note: File allocation table and structure of directory entries, in detail, are the topics of the next article.

The values above, are calculated and assigned to their variables between the lines 192 and 228. The memory addresses of these variables are 7BD2h, 7BD6h and 7BDAh, respectively. Next, entire root directory is read into the memory (lines 238 - 243). The value of DI after pop instruction in the line 240, comes originally from the value of AX pushed in the line 221. This value is the length of root directory, in sectors, when calculating the data_start value. The code between the lines 249 and 263 is searching for KERNEL.SYS file in the root directory table. If the file is found, the cluster number containing the file is pushed to the stack (line 263) and the entire FAT is read from the disk (lines 277 - 290) in size of "sectors per FAT" sectors.

Here comes the exact point, where the hard disk (FAT16) boot code differs from floppy disk (FAT12) boot code in FreeDOS. These codes probably exist in format.com as precompiled for both FAT12 and FAT16. I will explain the structure of this table and how it's read and interpreted in the next article. Basically, this table holds the consecutive cluster number or an "end of file (EOF)" mark, similar to linked list. With the help of first cluster number of KERNEL.SYS, following cluster numbers are extracted from file allocation table. The list of these clusters is written to a list at 0060h:2000h word by word (lines 301 - 327). This list is then read in a loop (lines 349 - 355) and the sectors corresponding to the clusters in the list are loaded to the address 0060h:0 and the kernel is run with the far jmp in line 353.

Note that because this code is relatively complex (and long), the only error message is simply an "Error!." (line 379). In the readDisk function, a dot char is printed on the screen for each sector read. This is probably necessary to pinpoint an unreadable sector. LBA support is checked in the readDisk function, but LBS isn't supported on floppies and this test is skipped anyway when boot media is a floppy disk (line 416). If LBA is supported, reading the disk is done using the DAP package created in an earlier phase and related LBA read function of int 13h. Otherwise, read_normal_BIOS (line 442) function is called. In this function (lines 445 - 486), the dword value, hold in LBA_SECTOR_0 and LBA_SECTOR_16 adresses is converted to CHS address. This function reads each sector to ES:63A0h and then copies it to the destination address in ES:BX (lines 493 - 500).

FreeDOS also supports FAT32 drives. In the previous article, I intentionally disabled FAT32 while installing the VM. Therefore, current disk boot sector is same as floppy boot sector, except for the FAT16 code block, mentioned above. There are two other boot sector codes named boot32.asm and boot32lb.asm for FAT32 disks. If I ever analyze them, I will do it in a separate article.


b. DOS Boot Code
DOS 6.x boot code is still under copyright and therefore there is no source code of it available but it is simpler than FreeDOS code. Even though it is under copyright, it can be disassembled in linux with following commands:

dd if=floppy.ima of=floppy.bin count=1 bs=512
objdump -M intel -D -b binary -m i8086 --adjust-vma=0x7C00 floppy.bin > floppy.asm

The variables in the boot block needs to be separated from the code. This code has to calculate the same variables to find the starting address of IO.SYS file. I don't think that this code is capable of using LBA because its simplicity. The problem with MSDOS is, it is not flexible as FreeDOS in some terms because it is running under some constraints to keep the code short but on the other hand it is very susceptible to something wrong:

  1. DOS requires the entries of kernel files (IO.SYS and MSDOS.SYS) to exist at the first and second place in the root directory listing. Even if these files are in disk, DOS boot will fail if their entries are not in first and second place. So, if I copy IO.SYS and MSDOS.SYS to a floppy or a disk manually, the boot process could fail. FreeDOS, on the other hand, allows booting even if KERNEL.SYS is copied manually as long as boot sector code searches for it.
  2. DOS also requires the IO.SYS file to be not fragmented. Most probably some sequential sectors are read instead of calculating the next cluster in which the continuation of the IO.SYS exists. If IO.SYS is fragmented, there could be no guarantee that the file read into memory is really IO.SYS. This also makes the system vulnerable to viruses.
  3. If only some parts of IO.SYS is loaded to memory by the boot sector, this leads that IO.SYS is responsible for reading the rest of itself into the memory.

How Are These Structures in Linux?
None of these exist, in short. There is neither number of sectors, heads, reserved sectors etc, nor any executable code. There are boot loaders to keep the boot chain continuous between BIOS and the kernel. If I consider GRUB, (1) GRUB Stage1 code can be written in MBR. In this case, GRUB MBR code runs core.img, finds grub.conf file and loads the selected OS. Or (2), Stage1 code is preferably in the first sector of the /boot partition. In this case, that partition has to be marked as bootable in the partition table and standard MBR code loads Stage1 code from the first sector of the partition, then core.img is loaded by Stage1 code and first step continues.

The process is similar in LILO and syslinux. By the way, since UEFI itself is a boot loader, linux kernel can be called from BIOS via UEFI without the need of any boot loader on UEFI supported machines.


Appendix: The difference between VBR and Boot Sector
The "Boot sector" article in Wikipedia covers (more generally) the structure that I previously mentioned as MBR. The hat-note in the MBR article says: "This article is about a PC-specific type of boot sector on partitioned media. For the first sector on non-partitioned media, see volume boot record.". The hat-note in the "Boot sector" article says: "This article is about the generic concept of boot sectors. For the VBR in PCs, see Volume Boot Record. For the MBR in PCs, see Master Boot Record.".

Why has there been such a naming confusion? While I was learning these topics, I never encountered the term VBR. I have always used the terms "MBR" and "boot sector" to distinguish between these two data structures, as they are in the sources which I learned these from. I could not find any VBR term in the references of VBR article itself. In short, VBR should be a relatively new term. I also checked from Michael Tischer's "PC Intern 3.0: System Programming (1st edition, 1992)". On page 557, MBR is called "partition sector". The subject, I explained in this article, is also mentioned as "Boot Sector" in this book (page 919).

Tuesday, September 29, 2020

Mobile App Development with MIT App Inventor2 (Bluetooth Thermometer)


Hi there. A comment from a valuable friend to the previous serial thermometer article gave me the idea for this article. As you know, computers with serial port are no longer available. If I want to read the temperature data to my mobile phone, is there an app to read it installed in my phone? Maybe yes, there are applications which can read raw data from Bluetooth (BT) in PlayStore. How safe are these apps? Why does an applications whose only job is to send / receive data to/from BT wants to access my camera or my mic?

Since BluetoothSPP app, which I was using on my first smartphone, was not compatible with the Android version that I am using on current phone, I wrote a MobileApp for my BT remote controlled car. The only thing it does, is to drive the car back and forth, turn it or stop it by sending one byte messages (or characters to be more precise). For this app, I didn't need to learn Android programming from the scratch thanks to MIT App Inventor (AI2). With AI2, it is possible to develop MobileApps by dragging and dropping controls without writing a single line of code. Maybe it's not perfect tool, but ideal for developing something fast (prototyping). Is it safe? Well, I have not personally tested its security, but it seems unlikely that an institution like MIT will develop a platform that possibly contains malware. Moreover, if such a platform with ~60K active users per day (>8M users in total) had security issues, we would have most likely heard that immediately.

Before moving on to AI2, I will shortly explain the hardware I use and the software running on Arduino. By the way, in this article, I just wanted to show the proof of concept. This app works only "well enough", it is not perfect in terms of design because design is not my specialty. It would be simpler if I had designed the circuit
with a microcontroller instead of Arduino, but let this be a topic for another article (if I ever can). 


Parts List
Arduino Uno
Arduino Bluetooth Shield
2x 4.7K Resistors
DS1621 IC
Breadboard
Dupont Jumper Wires (about a dozen)


Setting Up the Circuit
I based on the circuit, I have described in I2C with Arduino article and I added Keyes Bluetooth module to the circuit. Original schematic is below:

DS1621 Termometre
 
The BT module has 4 pins (figure right). RxD and TxD pins are connected to Arduino's D3 and D4 pins respectively. There is no special reason to do exactly so, they can be connected to any data pin by making necessary code changes. Remaining two pins are Vcc and GND, obviously.

By the way, a small donation is asked since about a year, to download Fritzing. The source code is still freely available on github for those who want to compile it. And I am not sure if it can be compiled on Windows without any problem. The files have not yet been deleted from the fritzing.org server but there are no links available to the older versions on their website (they can be found with some search). Older versions like v0.8.5 and v0.9.2 are available in Linux Mint and Fedora repos. The amount of donation is just 8€, by the way. It is not very expensive at all. Personally, I did not pay, as it is already installed in my computer (although it is a bit old), but I can choose to pay in future. Installation files can also be found on third party sites, but I think those files are not really safe.


The whole circuit is simply on the image below:

I copied the code running on Arduino, from the I2C article and added about 16 more lines of code for BT connection. To connect BT module to a different pin, to const definitions at the tenth and eleventh lines has to be changed.  

 /* DS1621 temperature sensor interfaced by Arduino
 *
 * I2C ID will be 0x48 if A0 = A1 = A2 = GND
 */

#define DS1621 B1001000
#include <Wire.h>
#include<SoftwareSerial.h>  // For BT module

const int BTRxD = 3;        // BT RxD Pin
const int BTTxD = 4;        // BT TxD Pin
// Define a SoftwareSerial object
SoftwareSerial Serial1(BTTxD, BTRxD);

void setup()   {
  // I2C initialization
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(DS1621);
  Wire.write(0xAC);     // Send Access Config command
  Wire.write(0x02);     // Write 02 to config register.
                        // Output polarity bit = 1 => Active High
  delay(10);

  Wire.beginTransmission(DS1621);
  Wire.write(0xEE);     // send "start convert" command
  Wire.endTransmission();       // Stop bit
 
  BluetoothInit();
}

void BluetoothInit()    {   // Bluetooth initialization
  Serial1.begin(9600);
  Serial1.flush();
}


void loop()  {
  float degree;
  byte SH, SL, X;
  char buffer[8]; // BT buffer
  // SH: High order byte of temperature
  // SL: Low order byte of temperature

  Wire.beginTransmission(DS1621);
  Wire.write(0xAA);     // Send "Read Temperature" command
  Wire.endTransmission();

  Wire.requestFrom(DS1621, 2);
  // After sending "read temperature" command
  // request two bytes from IC for temperature
  X = Wire.available();
  if(X >= 2)  {                 // if 2 bytes were received
    // SH contains the integer part of the temperature
    SH = Wire.read();
    // SL contains 0x80 for 0.5 degree celcius
    SL = Wire.read();
  }

  delay(500); // read the temperature from BT and send two times per second
 
  degree = (float)SH + float(SL) / 256.0;
  dtostrf(degree, 2, 2, buffer);  // convert degree to string
  Serial.println(buffer);
 
  // Send the data in buffer to BT
  Serial1.println(buffer);
  Serial1.flush();

}

This program simply retrieves the data from the temperature sensor, converts it into degrees as float, then the float value is converted to string and it is sent to both serial port and BT, which I defined as Serial1 object. I compile this code and send it to Arduino. The light on the BT module will flash becuase no BT clients are connected yet.


Designing the App using MIT App Inventor2
In this chapter, I will explain how the MobileApp is developed. First steps to configure AI2 are explained at https://appinventor.mit.edu/explore/ai2/setup. I need;
1- to go to http://ai2.appinventor.mit.edu/ and login with google account. I don't really like that this page is served in HTTP instead of HTTPS but there is nothing to do about this. It would be also nice if we could open our local account on this site instead of google account.
2- to download and install MIT AI2 Companion app from PlayStore. As far as I know, AI2 does not support iPhones. It is still possible to develop Apps on WebUI without any phone and AI2 Companion, but AI2 Companion is required to try the developed application on the smartphone.

I will use the setup described in the first option in the link above. First, I installed the app to my phone. It is stated that the computer and the phone must be connected to same WiFi network. My computer is connected with cable and everything works fine. So, I must correct this so that they has to be on same subnet.

Now I click  to "Start new project" and create one. I named my project ThermoBT. MobileApp will be named after the project name. The project will be opened right after it is created. If not, it can be opened from "My Projects" menu. After the project is opened, click "Connect -> AI Companion" as shown on the image right. Then a QR code and password will appear on the screen. Now, when I open the app on my phone and scan the QR code, AI2 connects to my phone. From now on, everything I do on web interface, will immediately appear on my phone.

On the left side of the web interface, there are components needed to build a GUI for the app. When these components are dragged and dropped onto the virtual phone on the screen, they are placed into the list on the right side of the screen (Components menu). To align the components to be added, I need to place some "Arrangements" from Layout tab. These can be thought as invisible tables. I add two "HorizontalArrangement"s, one below the other and one "VerticalArrangement" under two "HorizontalArrangement"s. I change their names to distinguish them when I use the same component more than one. I renamed the top "HorizontalArrangement" to "HorizontalArrangementTemp", the middle one to "HorizontalArrangementControls" and the bottom one to "VerticalArrangementMain". There is a "Rename" button in the "Components" menu. Finally, I place another VerticalArrangement in VerticalArrangementMain and rename it to VerticalArrangementSub. I used this youtube video below to understand how to use Arrangements. By the way, youtube is the best resource to learn AppInventor:



Now, I go back to the components list on the left hand side and place two "Labels" side by side from "User Interface" tab into "HorizontalArrangementTemp" which is at the top of the virtual phone screen. Then I click either to Label1 on the virtual phone or to its corresponding entry in the component list and locate the "Text" property in the rightmost "Properties" section and change it to "Temperature: ". Similarly, I remove Label2's Text field blank and set the height of the Label2 to 50%. 
 

Finally, I click Width of HorizontalArrangementTemp's properties, select "Fill Parent", and click OK. So, I am done with this box.

Then, I drag a "CheckBox" and a "Button" next to each other, into the second arrangement. Of course, everybody is free to design as they wish, but paying attention to the naming is needed while writing the code. I rename the button object to "ButtonReadTemp" and change its text to "Read Temperature". I do not change the name of the check box object but change its text to "Read automatically". After clicking "Width" property from "HorizontalArrangementControls" setting it to "Fill Parent", I am done with this box, too (below).


Now, from the properties of VerticalArrangementMain, I assign "Center: 3" to "AlignHorizontal" and "Bottom: 3" to "AlignVertical" property and I select "Fill Parent" for both "Height" and "Width" properties. VerticalArrangementSub (inside), falls to bottom middle. I set its "AlignHorizontal" property to "Center: 3" and insert a ListPicker and a button object. The placement sequence does not matter for these two objects. I change the button text to "Disconnect from device" and the text of ListPicker to "Select Bluetooth device". After that I am done with this box, too.

From the components list on the left, I drag "Clock" from the "Sensors" tab, drop it to the phone and drag "Bluetooth Client" from the "Connectivity" tab drop it to the phone, too. These are invisible controls. I click to Clock from the components list, turn the TimerEnabled property off and set TimerInterval to 500. This means clock will be triggered every 500ms. Once everything is done, the screen should look like this:


There is no need to display temperature related components or "Disconnect" button to the user if the app is not connected to a BT device. Likewise, if the app is connected to a BT device, there is no need to show device list, either. Therefore, I go to the properties of HorizontalArrangementsTemp, once more and uncheck the Visible box at the bottom. I do the same to HorizontalArrangementControls and Button1 (Disconnect). Now, when the app is started, it only shows the device selection button. Other components only appear if BT connection is established. So far, the design is completed.


Coding with MIT App Inventor 2
I think the best feature of AI2 is, being able to create procedures with colored pieces of code lines, like playing lego, instead of writing code as we understand. In this section, while creating BT related controls, I used the video below:


To create procedures, I press "Blocks" button at the rightest side of the top green bar. I will program first ListPicker1, to show the list of BT devices when pressed. To do this, I select ListPicker1 from the list on the left and drag the ListPicker1.TouchDown block from events list to the white area. Then again, I drag "set ListPicker1.Elements to" block from the events list of ListPicker1 to the workspace. Finally, I place "BlutetoothClient1.AddressesAndNames" (light green) from the list of BluetoothClient1, to the left side of dark green "set ListPicker1...". After one piece is interlocked to other one (like a jigsaw puzzle piece), I attach them into the fitting place next to "do" statement in brownish "when ListPicker1.TouchDown" block.


I previously wrote that whatever is added on the web interface of AI2, is reflected in the application, seen on the phone, simultaneously. The same applies to codes. The codes are also sent to the phone at the same time when adding / deleting a block etc. Previously, I had connected the Arduino to my computer and wrote the code to the chip. When I return to my phone and select "Select Bluetooth device" in the app, I am developing, it will list BT devices (of course BT needs to be enabled on the phone) but even though the devices are listed, the connection code is not written yet.

I will use the ListPicker1.AfterPicking event to connect to the device, and inside it, I place a "if ... then" block from Built-In -> Control. Then, I add purple colored .Collectaddress function block from BluetoothClient1 functions next to the "if" block, I just placed and finally, I add ListPicker1.Selection value (light green) next to it. "connectaddress" function will be called and if the function succeeds, the block next to it will be executed afterwards. I will not call any BT handler or anything else related with BT within "then" block, but I need to make some visual adjustments.

1- Since I will not connect to another device, I have to set the ListPicker1 invisible:
set ListPicker1.Visible to false
2- I have to set the Disconnect button visible:
set Button1.Visible to true
3- I have to set Arrangements visible:
set HorizontalArrangementTemp.Visible to true
set HorizontalArrangementControls.Visible to true
4- In order not to read the temperature automatically at first:
set CheckBox1.Checked to false

True and False values can be found in Built-In -> Logical. And dark green boxes can be placed vertically like this:


Now, selecting HC06 (Arduino BT module) from BT device list in the phone app, will cause the BT module light stop blinking because the phone is successfully connected to BT module. But when I press "Disconnect" nothing still happens because Disconnect is not implemented yet. The disconnection procedure should be the opposite of the connect function above. BluetoothClient1.Disconnect needs to be called and I will set the visible objects invisible, which set visible above and vice versa. To keep the article short, I only give the related code block:
 

So far, the application also disconnects. Now it is the time for the most important procedure: reading data from BT. I will use Click event of ButtonReadTemp. Before reading any data, I have to check that BT is still connected and there is data available in the buffer. This step is important because even after the initial connection is made, the device might be physically disconnected (or unavailable, for example the device went out of range). Therefore, I add an "if ... then" block into the ButtonReadTemp.Click event block. Then I place an AND logical conjuction block from Built-In -> Logic. The first condition is BluetoothClient1.IsConnected. The second condition is whether some data is available in the buffer for reading. To check this second condition, a mathematical comparison is needed which is actually the equal block from Built-In -> Math. After placing this, I change equal to "greater than". I place BluetoothClient1.BytesAvailableToReceive to the left hand side of the inequality and 0 (zero) to the right hand side. Zero is available in "Math" section of the list.
 
When all these conditions are met, the data will be read from BT and written to Label2. In the end, the ButtonReadTemp will look like this: 


When I go back to the app and press "Read Temperature" button, if the app has been connected to the module for a long time without reading any data, temperature values accumulated in the buffer, will fill the screen out. That is the reason why I limited the height of Label2 to the half of the phone screen. As I wrote at the beginning, I tried to quickly develop something for this project, therefore the result is not perfect.

Since the buffer is filled quickly, the best would be to read the data automatically. For this, I add a procedure to the CheckBox1.Changed event. If CheckBox1.Checked is true, then I need to enable Clock1 and disable ButtonReadTemp or vice versa. This is pretty straightforward compared to other procedures. First, an "if ... then ... else" block is required for this. This block can be either found under "Control" section, or it can be also created by pressing the blue gear icon at the upper left of an "if ... then" block and then dragging the "else" into the "if" block on the right.


Finally, if Timer is enabled, Clock object needs to do what originally the ButtonReadTemp.Click does: 


To create the above procedure in an easy way, I right click on the "if ... then" block, I created for the "Read temp" button, select "Duplicate" and insert the duplicated block into the Clock1.Timer event. That's all. If this application is finished completely, I can download it as an .apk file to my phone and use it without AI2. For this, you need to click "provide QR code for .apk" from the Build menu in web interface. After the app is compiled, a link to download the .apk and a QR code containing this link, are displayed. At this step, if AI2 Companion app is still connected, you need to disconnect to scan new QR code (using Connect -> Reset Connection or by closing and reopening the app on the phone). When I reopen AI2 Companion and scan the QR code -if the security settings allow apk installation on the phone- the app will be installed and can be used independently after granting requested permissions.

The application, created, can be downloaded to the computer using "Export selected project (.aia) to my computer" under "My Projects" menu. The project file, I created, in this article can be downloaded from here.