Entries tagged 'cat:Projects' (Page 2)

SBWG 1.0.0 Delayed To Next Life

Entry created on 2021-12-28 Authors: steeph (376) Categories: Bash (31) Code (31) Computer (78) Linux (36) Projects (41) SBWG (18) Scripts (28) Software (53) Languages used: en (257) Topics: Projects → Code → Bash Scripts → SBWG (16) edit

I've decided to not publish version 1.0.0 of SBWG.

Very early on I devided against a versioning approach that allowed me to stay below version 1.0 for a very long time by only incrementing the version number by 0.0.1 for important changes but rather chose to increment the version and publish a new version whenever. I stick to this approach because it allowes me to express felt overall progress in the version number. But now that the goals that I at some point set for version 1.0.0 are nearly reached (they pretty much are), this leaves me in a spot where publishing version 1.0.0 would be the next thing to do despite the fact that it's not actually really absolutely perfect, yet. Absolute perfection isn't really my approach. It's still more like a learning project.

So to avoid having published a version that looks perfect on the label but isn't inside, I will skip this version number. Don't get me wrong. My goals are. I've tested it more than I thought I would and thought of potential problems and fixed bugs that I'd argue are not something you'd expect most shell scripters to catch. It definitely works reliably for what it is intended and at least works well for much more than I thought it would in the first few months of starting this project.

So, there is no link in this entry. No new version published today. The next version will be 1.x.x something.

Alright. Now that that's done, I can start to implement new features again next year.

Comment via email

backup.sh

Entry created on 2021-12-05 Authors: steeph (376) Categories: Backups (4) Bash (31) Code (31) Computer (78) Linux (36) Projects (41) Scripts (28) Software (53) Languages used: en (257) Topics: Projects → Code (26) edit
This entry is an update of the entry 'Backup Shell Script'.

I've updated/improved my backup script again.

Download


#!/bin/bash
#2021-11-03

budir="/mnt/backup"
num=8
list="/root/backup-list"
hostname=$(</etc/hostname)
srcdir="$1"
#NAME=${SRCDIR//\//--}
name="$2"

function usage () {
  echo "This script requires either one or two arguments."
  echo "Usage:"
  echo "$0 JOBLIST"
  echo "$0 SOURCE_DIRECTORY NAME"
  echo
  echo "JOBLIST File that contains one backup job per line in the format SOURCE_DIRECTORY NAME"
  echo "SOURCE_DIRECTORY Directory that should be backed up. Please, no trailing slash."
  echo "NAME A string that is used to name the backup in the destination."
  exit 1
}

function single() {
  if grep -qs "$budir " /proc/mounts
  then
    fullname="$budir/$hostname/$name"
    printf "\n"
    printf "Attempting backup of source directory '%s' to '%s'.\n" "$srcdir" "$fullname"
    printf "Number of differential backups to keep: %s\n" "$num"
    printf "Removing oldest backup... "
#    rm -rf "$fullname.$num"
    rsync --archive --delete "$budir/empty/" "$fullname.$num/"		# This is quicker than rm.
    printf "Done.\n"
    for ((i=$num; i>=2; i--)); do
      printf "Renaming '%s' to '%s' ... " "$fullname.$((i-1))" "$fullname.$i"
      mv "$fullname.$((i-1))" "$fullname.$i"
      printf "Done.\n"
    done
    printf "Duplicating last backup ('%s' to '%s')... " "$fullname.0" "$fullname.1"
    [[ -d $fullname.1 ]] && exit 1							# This directory should not exist at that point.
    cp -al "$fullname.0" "$fullname.1"
#    rsync --archive --acls --xattrs --hard-links "$fullname.0/" "$fullname.1/"
    printf "Done.\n"
    now=$(date)
    printf "\n\n\n" >> "$fullname.log"
    printf "STARTING INCREMENTAL BACKUP AT %s\n" "$now" >> "$fullname.log"
    printf "Starting new incremental backy uppy at '%s.0'..." "$fullname"
    if rsync --exclude ".cache" --archive --no-links --delete "$srcdir/" "$fullname.0/" 2>&1 | tee -a "$fullname.log"
    then
      printf " Done.\n"
      printf "Done.\n" >> "$fullname.log"
    else
      printf " Failed.\n"
      printf "Failed.\n" >> "$fullname.log"
    fi
    now=$(date)
    printf "\nIf this line is here the script finished (with or without errors) at %s\n" "$now" >> "$fullname.log"
  else
    printf "'%s' is not mounted. Aborting." "$budir"
    exit 1
  fi
}

function fromlist() {
  while read job; do
    if [ -n "$job" ]; then
      name="${job#* }"
      srcdir="${job% *}"
      single
    fi
  done < "$list"
}

mkdir "$budir/empty"									# Empty directory for a quicker method to delete a large directory.

case $# in
0)
  mount "$budir"
  printf "Reading backup jobs from list. Defaulting to %s.\n" "$list"
  fromlist
  ;;
1)
  mount "$budir"
  list="$1"
  printf "Reading backup jobs from %s." "$list"
  fromlist
  ;;
2)
  mount "$budir"
  single
  ;;
3)
#  I can't decide whether to make the third argument num or budir. I don't need it anyway.
#  num=$3
#  budir=$3
#  mount $budir
#  single
  usage
  ;;
*)
  usage
  ;;
esac

exit 0
Comment via email

SBWG - The Pathshortener And Other Recent Changes

Entry created on 2021-12-05 Authors: steeph (376) Categories: Bash (31) Code (31) Computer (78) Linux (36) Projects (41) SBWG (18) Scripts (28) Software (53) Languages used: en (257) Topics: Projects → Code → Bash Scripts → SBWG (16) edit

I made it my goal to harden SBWG before I start to implement new features. Before I call the next version of this project 1.0 I want to make sure that unexpected input from the command line or from source files, absurd numbers of absurdly long tags and content items, stupidly weird filenames or random binary data as tag values as well as purposfully created traps in the various places where input is processed are handled well, meaning that nothing fails unless there is no sensible way around it, and if something fails, that nothing breaks. Data should be filtered carefully, errors should be handled well and whereever possible data should be made processible if it was supplied in an unprocessible form to reduce the chances of errors. On top of that I wanted to make sure that the script did its job in a reasonable amount of time considering the circumstances. I mean, it will never be very fast. Bash is just not the right language for that. But there certainly were some repetitive tasks that could be improved. Fot the latter I created a simple caching functionality that will probably be extended in the future. I managed to reduce the (calculated/estimated) generation time of my biggest test web site from almost 300 years to a few days. Actual web sites will of course not take that long to generate, even on a slow machine. A huge web site will maybe take up to one day to generate completely, even without the new options that keep the script from re-generating existing unchanged parts of the web site. But before sombody will try to create such a big web site with SBWG I will probably have improved speed further. And even then it's a worst-case time.

As part of the aforementioned goals I have started working on last new feature before version 1.0. I call it the pathreducer. Since many of the files created by SBWG are named after the tags they represent, they can become quite long and contain almost any printible character, including multi-byte unicode characters or characters of character sets I haven't even heard of. I definitely don't want to restrict more than I already have what characters and how many of them tag values can contain. Especially filesystems used by operating systems from Microsoft are relatively restrictive in maximum allowed directory, path and filename length and allowed characters. By default the pathreducer is not used. But if enabled via command line option or in a web site's settings file, it will filter directory and filenames and shorten them to a user-defined maximum length. If the pathreducer decides to change a path elements it also adds a 6-character hash value to make shortened or otherwise reduced path elements as good as unique.

That works well for now and even can create 8.3 or 6.3 filenames for old DOS filesystems. But the result is not very nice because it isn't aware of what filesystem it is going to write a file to. To be save it removes more characters than it would have to for ext and NTFS filesystems. In the future I may extend the pathreducer to detect the filesystem at least of the root of the output directory automatically and decide how exactly path elements should be reduced according to the actual limitations of the present filesystem. Than it may even be enabled by default, even though it can increase the generation time quite a bit.

There are still some tests that I want to do and I will probably find some more things that I want to fix before version 1.0. But I see light.

Comment via email

USB/Serial PWM Fan Controller Using an Arduino

Entry created on 2020-11-22 (edited 2021-10-15) Authors: steeph (376) Categories: Arduino (4) Case Modding (11) Code (31) Computer (78) DIY (17) Electronics (16) Fans (1) Hardware (16) Projects (41) incomplete (22) Languages used: en (257) Topics: Projects → Arduino (4) Projects → Case Modding (9) edit

I wanted to be able to control the speed of the fans in my big NAS, Fred, individually. Even though the mainboard in use has five PWM fan connectors, the chipset can only control the speed of all fans together. There are probably good fan controllers commercially available that solve this problem better than I did. But they seemed overpriced and it seemed like a fun learning project for me.

The fan controller that I made uses an Arduino Nano clone that listens to it's serial port, waiting for a command to change the speed of a fan. When a command is recognised the continuous PWM signal for that fan is changed accordingly. It's possible to control up to six fans this way with an Arduino Nano. I'm only using three though since I only have three fan groups that need to be controlled separately.

The Arduino sketch/C code for the Arduino Nano that I used is as follows.




//fan speed sensor wire attached to digital pin 2 with a 10kohm pullup resistor
//fan PWM control wire attached directly to digital pin 9

#include <PWM.h> //include PWM library http://forum.arduino.cc/index.php?topic=117425.0

volatile int half_revolutions1; //allow half_revolutioins to be accesed in intterupt
volatile int half_revolutions2; //allow half_revolutioins to be accesed in intterupt
int rpm1; //set rpm as an integer
int rpm2; //set rpm as an integer
int pwm=255;
const byte numChars = 5;
char receivedChars[numChars];

boolean newData = false;

void setup()
{
  InitTimersSafe(); //not sure what this is for, but I think i need it for PWM control?
  bool success = SetPinFrequencySafe(9, 25000); //set frequency to 25kHz
  pwmWrite(9, 51); // 51=20% duty cycle, 255=100% duty cycle

  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  analogWrite(5, 170);
  analogWrite(6, 255);
  pinMode(2,INPUT_PULLUP); //set RPM pin to digital input
  pinMode(3,INPUT_PULLUP); //set RPM pin to digital input
  half_revolutions1 = 0;
  rpm1 = 0;
  half_revolutions2 = 0;
  rpm2 = 0;

  Serial.begin(9600);
}



void loop()
{
  sei(); //enable intterupts
  attachInterrupt(0, fan_rpm1, RISING); //record pulses as they rise
  delay(1000);
  detachInterrupt(0);
  attachInterrupt(1, fan_rpm2, RISING); //record pulses as they rise
  delay(1000);
  detachInterrupt(1);
  cli(); //disable intterupts

  rpm1 = (half_revolutions1/2)*60;

  Serial.print("1");
  Serial.println(rpm1);

  rpm2 = (half_revolutions2/2)*60;

  Serial.print("2");
  Serial.println(rpm2);

  rpm1 = 0;
  half_revolutions1 = 0;

  rpm2 = 0;
  half_revolutions2 = 0;

  pwm = 255;
  recvWithStartEndMarkers();
  processCommand();
}

void fan_rpm1()
{
  ++half_revolutions1; //increment before returning value
}


void fan_rpm2()
{
  ++half_revolutions2; //increment before returning value
}


void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = 's';
    char endMarker = '\n';
    char rc;
 
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void processCommand() {
    if (newData == true) {
        Serial.print("s");
        Serial.println(receivedChars);
        switch (receivedChars[0])
        {
            case '1':
                receivedChars[0] = '0';
                sscanf(receivedChars, "%d", &pwm);
                analogWrite(5, pwm);
                break;
            case '2':
                receivedChars[0] = '0';
                sscanf(receivedChars, "%d", &pwm);
                analogWrite(6, pwm);
                break;
            case '3':
                receivedChars[0] = '0';
                sscanf(receivedChars, "%d", &pwm);
                
                break;
//            default:
//                Serial.println("I don't know what that means.");
        }
        newData = false;
    }
}

Well, how should I put it? It works, usually.

(tbc?)

(tba:photos)

Comment via email

Zeo Mobile Teardown

Entry created on 2021-07-14 (edited 2021-10-15) Authors: steeph (376) Categories: Electronics (16) Projects (41) Shared (17) ZEO (1) Languages used: de (90) edit

2015 habe ich mir mal ein Zeo Mobile genauer angeschaut und im Schlafhacking-Blog darüber geschrieben.

Hintergrund: Das Zeo war ein Gerät ("war", weil es schon lange nicht mehr hergestellt und vertrieben wird), das anhand von Bewegungsdaten, EOG und EEG ein Schlafprofil des Trägers eines Stirnbandes erstellte. Es war wahrscheinlich das erste Gerät seiner Art, das diesen Einsatzzweck einfach benutzbar, mit cloudbasierter Software für Normalconsumer und - dafür, dass es quasi kein richtiges Konkurrenzprodukt gab - ziemlich günstig, bedient hat. Ich weiß gar nicht, woran der Hersteller, Zeo, Inc., ehem. Axon Labs) gescheitert ist. Er war dem heutigen Trend so weit voraus und hat alles geboten, was den Benutzern von heutigen, ähnlichen Geräten für den Zweck der Verbesserung der Schlafqualität wichtig ist, dass man ihn eher als Innovator als als seiner Zeit voraus bezeichnen kann. Anyways, es gab/gibt hauptsächlich zwei Geräte: Das Zeo Bedside, bestehend aus Nachttisch-Empfangsgerät, Kopfband, USB-/serieller Schnittstelle und Windows-Software, und das Zeo Mobile, ein Bluetooth-fähiges Kopfband mit Smartphone-App. Für ersteres wurde von einem (Ex-)Entwickler des Gerätes eine modifizierte Firmware (Version 2.6.3R) in Umlauf gebracht, die es ermöglicht, über USB die Rohdaten der Sensoren zu empfangen und so eigene Software zu benutzen. Damit hat während des kommerziellen Untergangs Zeos nicht nur er meinen Respekt gewonnen, sondern auch das Zeo Bedside neue Einsatzmöglichkeiten und eine Fan-Community von technik-interessierten Klarträumern. Für das neuere Zeo Mobile gibt es zwar auch eine Android-App, mit der man das Gerät ohne Konto auf dem nicht mehr existeierenden Zeo-Server weiterbenutzen kann, aber aus dem einfachen Grund, dass es nicht einfach möglich ist, Rohdaten aus dem Gerät zu bekommen, haben die Zeo Mobiles bei weitem nicht die Beliebtheit unter Hobbyisten, die die wenigen noch in Umlauf befindlichen Zeo Bedsides genießen.

Also, 2015 habe ich mir mal ein Zeo Mobile genauer angeschaut und im Schlafhacking-Blog darüber geschrieben: Zeo Mobile Teardown

Eigentlich wollte ich noch einige weitere Links hier einbauen, wie zum Beispiel zu der Entwickler-Webseite und zu ein paar interessanten Software-Projekten, die die Daten der Rohdaten-Firmware des Zeo Bedside nutzen. Leider kann ich die Firmware selbst nicht mehr finden und auch die relevanten Informationen sind nur noch im Internet Archive zu finden. Dort sind die Software-Downloads aber nicht archiviert, weshalb sich der Rest erübrigt. Sehr schade. Ich hoffe jemand wird die Sachen noch mal öffentlich zur Verfügung stellen. Ich habe sie leider nicht bei mir, weil ich nie ein Zeo selbst bespielt habe.

Vielleicht nehme ich den letzten Absatz wieder zurück. Hier ein paar URLs, die oben vielleicht fehlen (habe gerade keine Lust, sie in line zu bringen):

  • https://www.gwern.net/docs/zeo/index
  • https://www.gwern.net/docs/zeo/firmware-v2.6.3R-zeo.img
  • https://www.openyou.org/2013/06/11/zeo-firmware-and-raw-data-api-on-openyou-github/
  • http://eric-blue.com/2013/06/09/life-beyond-zeo/
  • https://github.com/evsc/zeoLibrary
  • https://github.com/openyou/zeo-firmware
  • https://github.com/openyou/zeo-raw-data-api
  • https://github.com/openyou/zeo-android-api
  • https://www.klartraumforum.de/forum/showthread.php?tid=9523
  • http://www.sleepstreamonline.com/rdl/
  • https://web.archive.org/web/20120422001938/http://sourceforge.net/projects/zeorawdata
  • https://web.archive.org/web/20110307193953/http://sourceforge.net/projects/zeodecoderview
  • https://sourceforge.net/projects/rxtxlibrary/files/rxtxlibrary/
  • https://sourceforge.net/projects/zeolab/
  • https://sourceforge.net/projects/zeolib/
  • https://web.archive.org/web/20120924070141/http://developers.myzeo.com/raw-data-library/
  • https://www.klartraumforum.de/forum/forumdisplay.php?fid=29 (Inhalt nur eingeloggt sichtbar)
Comment via email

SBWG 0.9.6

Entry created on 2021-08-29 Authors: steeph (376) Categories: Bash (31) Code (31) Computer (78) Linux (36) Projects (41) SBWG (18) Scripts (28) Software (53) Languages used: en (257) Topics: Projects → Code → Bash Scripts → SBWG (16) edit

As foreseen I've made slow progress in development of SBWG, the script that generates this web site, because I want to test and improve it with the current feature set before I start to implement new features. The former is necessary. The latter is more fun. But today I've reached a point where I can say that the only thing left to do before I call it v1.0.0 is testing edge cases and things that I didn't think of testing before, as well as fixing potential bugs discovered from this testing.

So, version 0.9.6 is out, everything is working, The README file, other documentation included in the package, the example website, the included style sets and partly even the code quality has been deemed satisfactory, and I hope I'll find the time to test all sorts of weird stuff and discover and fix some bugs next month, at which point version 1.0.0 will be published and I'll finally be able to allow myself to start working on new features, some of which are awaited by both regular users of the script.

Comment via email

Another never finished electronics project: my electronic typewriter that would make a very nice retro style terminal but isn't one

Entry created on 2021-06-13 Authors: steeph (376) Categories: Diskettes (1) Electronics (16) Keyboards (10) Projects (41) Retro Computing (6) Technology (5) Terminal (2) Typewriter (1) Vintage (3) Languages used: en (257) Topics: Projects → Ideas (8) edit

Background story: I wanted to park my car at a certain small parking lot that's usually pretty full. I was lucky: There was a free spot. Or so it seemed. As I drove up to it I saw there was something lying in the middle of the free spot. Looked like somebody got rid of some scrap there. I got out and honestly I wanted to put the stuff to the back of the parking spot under the tree that was there. If somebody decided not to bring their scrap to the right facility for free for some reason that doesn't make me responsible to pick it up and bring it there, I thought. But when I saw that it was an almost new looking electronic typewriter, I quickly decided to take it with me instead. It was a Triumph Adler Gabriele PFS. As I read somewhere it came out in the late 2000s or early 2010s. So not as vintage as the name typewriter may sound. But definitely some old piece of tech that is obsolete now but has a nice character and conveys a feeling from a certain time. That and the fact that I've never had or even used one made me add it to my collection of retro tech (that I've never before called collection because it doesn't really deserve that name).

Back at home I examined and tested it and I would have cleaned it, but there was nothing to clean. The case of the internal transformer was cracked and the outer case had a deep scratch at the corner where the transformer is. Everything was working fine except for the 2.8" diskette drive. The rubber band had long been degraded. But the bigger problem was that the main motor didn't do anything else than heating up very quickly. I couldn't find a replacement easily and still couldn't find a good replacement when I searched more thoroughly later. So I've replaced the drive with an emulator so I could use SD cards instead of diskettes. That not only has the advantage of working right now but also of using a medium that is more reliable being handled by my clumsy self and available easily. New 2.8 " diskettes would cost more than SD cards and couldn't hold nearly as many and long documents.

Before the drive emulator worked I had to work a bit with the developer of its firmware. I was lucky that I had a logic analyser and that the project was actively maintained by a developer who cared for adding support for my typewriter. The interface for that model of Mitsumi QuickDisk drive was already supported before. But the reverse engineered parameters, especially the timing was just not exact enough for my Gabriele.

Well, that's the state the machine is in right now. I can use it, I can save documents to SD card, I can't read the original diskettes and I can't read the documents on a PC or create documents for the typewriter on a PC without writing software for it first. I'm not using it for anything. The best thing I could come up with on what I could/would want to do with it is to add an interface that allows me to use it as a text printer. It has a printball with all important characters (no ASCII line art though, sadly) and maybe completely switch out its software to give the machine a new purpose. The easiest way for me would probably be to ignore all the electronics that are already inside and add a microcontroller that handles the LCD, print head and communication with the outside. I still wouldn't have any use for that though. So additionally I would turn it into a serial or telnet or SSH terminal, maybe all three. That would be more fun. Vintage look, vintage keyboard, vintage feel overall, a display that is subpar because it's old technology but still good enough to do its job, and the ability to print text on paper. Yeah, that would be fun. But I still wouldn't really have a use for it. I have a laptop that can do that job better and the keyboard of the typewriter doesn't have as many keys as I'd want it to have.

So, I'm not going to do it, or anything else with it. Maybe some day. Or none. Got any better/additional idea what I could do with it?

Comment via email

Project Idea And A Little Story: High Power PC Cooled Completely Passively With Heat Pipes And A Large Surface Aluminium Case And My First Online Post Ever

Entry created on 2021-06-02 Authors: steeph (376) Categories: Bulletin Boards (2) CPU Cooler (2) CPUs (5) Case Modding (11) Computer (78) Cooling (2) DIY (17) Heatsinks (2) Personal (13) Projects (41) Useless Memories (5) Languages used: en (257) Topics: Projects → Case Modding (9) Projects → Ideas (8) edit

Here is another project idea that I'll probably never realise. It's not really an ingenious idea or a new concept. But there is a reason for why I can't forget about it.

In the very early 2000s, when I started to tinker with PC cases and also made my first steps in web communities, I thought about how I could reduce the noise my computers made without running chips at dangurously hight temperatures or forgoing performance. I've read about heat pipes in some case modding community. And I thought why not take it to the extreme to move heat quickly not just to a larger heat sink than the CPU sockets could safely hold (Motherboards didn't have cooler brackets back then.) but to a heat sink or several heat sinks that cover the majority of the case's surface. When the first commercial CPU coolers with heat pipes came on the market, targetet at computer tinkerers, but still nobody in the community seemed to attempt to make a case with a huge heat sink on the outside of the wall to cool even a 2 GHz Pentium 4 with its 75 Watts TDP passively, I decided to register in a small case modding web forum and present my idea to see what might be wrong with the concept. I was actually younger 20 years ago than I am now and I had never before tried to get myself out there in such a way. I thought it was a rather good idea. But I wasn't sure how much surface and aluminium mass I needed and wether it was realistic to cool a powerful CPU passively that way. Trying to cool a Pentium 4 only passively sounds like a stupid idea after all.

So I created a post on said web forum that I've never read before, presented my idea and asked for opinions. I got a few answers and everybody seemed to think it was a stupid idea. One respondant didn't seem to get my idea but still seemed to think it was stupid. One person seemed rather friendly in comparison and asked if I could explain the idea in more detail. I felt bullied by the negative answers, I felt mocked by being inline quoted (which I don't think I had seen before) and I felt that my ideas were generally worthless since I wasn't one of those hobbyistic experts that actually know stuff and are able to answer questions asked in a web forum. So rather than explaining my idea in more detail as requested, I searched for a way to delete my post, didn't find one and asked in the same thread how I could remove it.

I didn't find the post when i searched for it a few years ago. Like most small web forums it has probably gone offline with nothing or almost nothing in a web archive. But with the experiece that I have today I suspect that I didn't explain my idea very well and the other forum members didn't realise that I was a very insecure child. I also realised many years later that it wasn't a bad idea. I even saw a computer case that implemented the same idea being sold at some point. I don't know if many people bought this. But at least somebody other than me seemed to think it made sense and could even be commercialised. This redeemed my idea in my mind and I started to think about making such a case again. But I don't have the need for high-power CPUs and didn't want to invest money into another project that I wouldn't ever finish once the initial exitement would be over by buying huge heat sinks and heat pipes. So I've added it to that huge imaginary list with projects that I like to would have done but likely wouldn't finish and conclude my decades long considerations and my decision to conclude them by writing this entry.

Done.

Comment via email
Go To Navigation Page
Show/Hide Navigation
Mastodon