Steeph's Web Site


Entries tagged 'cat:incomplete'

Alternative Operating System: Visopsys (VISual OPerating SYStem)

This entry is referencing the entry 'Alternative Operating Systems'.

Visopsys (VISual OPerating SYStem)

Visopsys is a real hobby OS created from the ground up. It's very likeable like this. It has a GUI that is kind of a mix of a MacOS-9-style and Windows-95-style desktop. There are a couple of accessories and a limited shell. A very promising OS from what I've seen and read. But also limited in its usability as of now, from my own experience.

I've tried Visopsys 0.91, the currently latest version on the download page. Running it in a virtual machine is recommended because of the limited hardware support. But I think that it is fair to hold an operating system to a standard that requires it to be able to run on real hardware, not on another operating system. I tried it on five computers before I got it to boot. I'm not going to list all the hardware components (I'd have to look up all the motherboards - meh), but the CPUs to get an idea for the age of the system components. First a Core2Duo. It printed bunch of error messages to the screen that I didn't further investigate. Initialisation failed and stopped. Then an AMD K6. I got a message informing me that Visopsys will boot in text mode because of a lack of SVGA support and then it rebooted. The used VGA card is a very old one. So that might be correct. Then I tried an Athlon 64 X2. It did something, the loading bar appeared, then it rebooted. I didn't get anything else out of it. Then I wanted to give The K6 a newer graphics card. But while digging for one, I found another PC with a Celeron E3200 and tried that first. And it worked. So I didn't bother checking the K6 again.

Boot time to live mode is about 3 seconds. Incidently, I also learned something about booting from CD. Booting from any ATAPI CD or DVD drive took many times longer than booting from the same CD in a SCSI CD drive. Like 10 times longer. That surprised me. I didn't think there would be such a big difference. Maybe my IDE cable has a problem or I did something wrong. Anyway, booting from USB or an IDE hard drive was quick enough to not bother to measure how quick exactly (about 3 seconds). Edit: A likely explanation for the load time difference between the SCSI and the ATAPI drives is that the ATAPI drives were all modern, fast-spinning drives that take a long time to spin up before any data is read, as apposed to the old, slow SCSI drives that I have, that allow reading from the disk much sooner.

Installing to a hard drive is straight forward: Optionally choose a language other than English (this also changes the keyboard layout), coose a partition, click a few next buttons, enter a password for the default user account and reboot. There is also a partitioning tool available from the installer. But I didn't find a way go get back to the installer without rebooting. After booting the installed Visopsys and logging in, or after booting in live mode, you get a simple desktop with a few icons and a task bar at the top.

There isn't much to do. As far as I know the included programmes are pretty much all that are available for Visopsys. (Please do correct me if I'm wrong!) There are system settings, a very limited shell with a limited number of commands with very limited options, a few casual games, an image viewer and a few other simple tools. The most useful things included apart from the file browser are probably the text editor, the calculator and the telnet client. There is no web browser and no multimedia applications.

During the time I used Visopsys, nothing crashed or went fundamentally wrong. But I noticed a couple of small things that would need to be sorted out to create a comfortable user experience. The most obvious one is that while dragging, the dragged objects sometimes were lost and dropped too soon. Especially dragging windows vertically has to be done very, very, very, very slowly. It's as if the mouse button had a loose contact otherwise. Another thing I noticed when choosing a new desktop wallpaper. Once the directory in which to look for image files to be used as wallpaper has been chosen, the file names seem to be cached. Image files that I've copied into that directory after that were not listed before rebooting. Little things like this add up to create a bit of an annoying experience.

But I don't want to talk down the state in which the project is in. Many things are working slawlessly and depending on what you need your computer to be able to do, it might even fulfill many of the needs. A web browser is not one of them, though.

I've attached some screenshots that I took. The one where the Snake icon is selected. should have a window with the Snake game in it as well. I don't know why it isn't invisible.

PIN Changer

I had to change the default PINs of over 200 SIM cards once. And such a situation could arise again. So I've built a PIN changer in which I just have to insert the card, wait a few seconds and it's done.

The Card Slots

SIM cards in their natural form factor aren't as fiddly to handle as they are in the form factors most people know, which is Mini SIM, Micro SIM and Nano SIM. Classical SIM cards are the same size as other smart cards. I found a card slot with an end switch on eBay. I like it when I find industry grade parts for cheap on eBay as part of some remaining stock. Additionally I've used a slide-in mini SIM slot and another, separate end switch from my parts collection in case I to change the PINs of smaller cards.

The Baseband Processor

Other parts that I've used is an Arduino Nano sized Arduino Nano nearly-clone and an A6 modem module. There are many similar modem modules designed around different but similar ICs. Many of them are cheap and widely used for DIY IoT projects. So example code for the Arduino and other help can be easily found on the web. I don't know why I went with a module with an A6. But it works fine and there are an Arduino library for it as well as cheap modem modules with it available.

(tba:voltage supply)

The Controller

Yes, Arduino may be kind of the noob go-to board and could look up how to use microcontrollers on their own finally and even if I don't want to I could finally start to use ESP32s like everybody else. But I know Arduinos and by now I'm familiar with it and it works, so, whatever. Arduino Nano is kind of my go-to form factor now because they have integraded USB, are Uno compatible and small. Unless I need more or something very specific I use Arduino Nano almost-clones with USB-C port.

The Code

The code is a real mess. It had been a long time since I had written any even halfway serious C. It may have been the first time, actually. The sketch surely is very easily improved by somebody who knows what they are doing. I intended to improve it myself. But the project is currently abandoned and The code is doing what it should in a way I initially had in mind as the goal. But I'll leave the mess of the comments in for the case that somebody wants to make out what I was thinking.

// Funktionen umschreiben: Beim Empfangen wird erwartet: 1. der AT-Command zurück, 2. eine Antwort, 3. OK oder ein ERROR.
//                         Daher sollte abgefragt werden, bis entweder OK oder ERROR kam oder 20/50/9001(?) Abfragen lang weder OK noch ERROR an kam.
//                         Die Antwort in Variable speichern? Naja, String zurückgeben halt.
//                         Antworten, die mit "^" anfangen brauchen nicht gehandlet zu werden, da keine Kommandos, die mit AT^ beginnen gesendet werden.


SoftwareSerial A6MODULE(6,7);
int intled = 13; // Internal LED
int successled = 8; // Green LED
int failled = 9; // Red LED
int wrongpinled = 10; // Orange LED

boolean debug=true;

//String commands[5] = { "AT", "AT+CPINC2", "AT+CPIN?", "AT+CLCK=\"SC\",2", "AT+CPIN=\"3010\"" };
//int command = 0;

const byte maxmsglength = 32;
char received[maxmsglength];
boolean newData = false;
String response = "";

int i=0;

To check/do:
1  AT: OK?
2  is PUK required - abort
3  are less than 3 PIN attempts left? - abort AT^CPINC=?
4  is PIN disabled
       5  enable it: 0000
6  is PIN enabled
       7  unlock
8  is card unlocked
       9  change PIN: 1996
              was PIN wrong - report and leave it

void setup() {
  pinMode(intled, OUTPUT);
  pinMode(successled, OUTPUT);
  pinMode(failled, OUTPUT);
  pinMode(wrongpinled, OUTPUT);
  // All LEDs turn on at the beginning and stay on during the wait period at the beginning, then turn off before communication with the A6 module starts.
  digitalWrite(intled, HIGH);
  digitalWrite(successled, HIGH);
  digitalWrite(failled, HIGH);
  digitalWrite(wrongpinled, HIGH);
  digitalWrite(intled, LOW);
  digitalWrite(successled, LOW);
  digitalWrite(failled, LOW);
  digitalWrite(wrongpinled, LOW);
  digitalWrite(intled, LOW);

void loop() {
  if(getfroma6("OK")) {
//    sendtoa6("AT+CPIN?");
//    if(getfroma6("+CPIN:SIM PUK")) { fail; }         // If the required password is PUK, abort.
//    sendtoa6("AT+CPIN?");
//    if(getfroma6("+CPIN:SIM PIN2")) { fail; }        // If the required password is PIN2, abort.
//    sendtoa6("AT+CPIN?");
//    if(getfroma6("+CPIN:SIM PUK2")) { fail; }        // If the required password is PUK2, abort.

    getfroma6("+CPIN:SIM PIN");                      // The last non-empty response will be stored in the global response variable. Problem with this: If the A6 module sends an unsolicitated message before the response to the CPIN command, nothing gets done and the card needs to be re-inserted again.
    if(strcmp(response, "OK") == 0) {                // If already no PIN is required

//      sendtoa6("AT+CLCK=\"SC\",2"); // Ist PIN-Abfrage eingeschaltet? Oder ist es "SC"?
//      if(getfroma6(???)) { PIN-Abfrage einschalten mit 0000; }

      d("I don't know how to enable the PIN.");

    if(response = "+CPIN:SIM PIN") {                 // If the required password is PIN, continue.
      if(getfroma6("^CPINC: 3")) {                   // If not exactly 3 attempts are left, abort. (should be larger than or equal to 3, shouldn't it?)
        sendtoa6("AT+CPIN=\"0000\"");                // Freischalten mit 0000
        if(!getfroma6("OK")) { fail; }               // If that was not the right password, abort.
        sendtoa6("AT+CPWD=\"SC\",\"0000\",\"1996\"");// PIN ändern
        if(getfroma6("OK")) { Serial.println("Looking good."); }

//      sendtoa6("AT+CMGD=0,4"); // Should delete all SMS
//      if(getfroma6(???)) { ; }
        d("I don't know how to delete SMS.");

        if(getfroma6("+CPIN:READY")) {

          digitalWrite(successled, HIGH);
      } else {                                         // If not exactly 3 times left

void fail() {
  digitalWrite(failled, HIGH);               // Turn red fail LED on and ...
  d("Something failed! Ending programme.");
  while(1);                                  // ... don't do anything anymore.

void wrongpin() {
  digitalWrite(wrongpinled, HIGH);           // Turn yellow LED on and ...
  d("Wrong PIN! Ending programme.");
  while(1);                                  // ... don't do anything anymore.

//boolean getfroma6(char str[32], char str1[32], char str1[32], char str1[32], char str1[32], char str1[32]) { // Returns true if the passed (expected) message was received, false if anything else was received.
boolean getfroma6(char str[32]) { // Returns true if the passed (expected) message was received, false if anything else was received.
  boolean asexpected = false;
    for (i = 1; i < 9; ++i) {
//    d("d1 "+response);

    if(received[0] == '\0') {                            // If the received message is empty
    } else {
      response = received;
    if(strstr(received, "ERROR") != NULL) {              // If the received message contains "ERROR"
      d("Received an error: "+response);
    if(received[0] == '+') {                             // If the received message starts with a "+" sign
      d("Reseived response: "+response);
    } else {
      if(strstr(received, "OK") != NULL) {               // If the received message is "OK"
        d("Received OK.");
        asexpected = true;                               // Also treat OK like the expected message. No unexpected OK should ever be sent from the A6 module. So this is fine. No, it is, really.
//        if(asexpected) { return true; }
      } else {
        if(strncmp(received,"AT",2) == 0) {              // If the received message starts with "AT"
          d("Received AT: "+response);
        } else {                                         // For any other received message
          d("Received: "+response);
    newData = false;
    if(strstr(received, str) != NULL) {
      d("Received expected message: "+response);
      asexpected = true;
  if(asexpected) { return true; }
  return false;

//void handleresponse() {
//  response = received;
//  if(received[0] == '+') {
//    d("               Response: "+response);
//  } else {
//    if(strstr(received, "OK") != NULL) {
//      d("               It's okay.");
//    } else {
//      if(strncmp(received,"AT",2) == 0) {
//        d("               I've sent: "+response);
//      }
//    }
//  }
//  newData = false;

void receivelinefroma6() {
  static byte counter = 0;
  char rc;
  received[0] = '\0';

  while (A6MODULE.available() > 0 && newData == false) {
    rc =;

    if (rc != '\n') {
      received[counter] = rc;
      if (counter >= maxmsglength) {
          counter = maxmsglength - 1;
    else {
      received[counter] = '\0'; // terminate the string
      counter = 0;
      newData = true;

void sendtoa6(String command) {
//  Serial.println(command);
  d("Sent: "+command);

void d(String line) {
  if (debug == true) { Serial.println(line); }


Analogue, Digital, Meaninglessness

(tba: the entire entry)

USB/Serial PWM Fan Controller Using an Arduino

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

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;


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

  rpm1 = (half_revolutions1/2)*60;


  rpm2 = (half_revolutions2/2)*60;


  rpm1 = 0;
  half_revolutions1 = 0;

  rpm2 = 0;
  half_revolutions2 = 0;

  pwm = 255;

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 =;

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                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) {
        switch (receivedChars[0])
            case '1':
                receivedChars[0] = '0';
                sscanf(receivedChars, "%d", &pwm);
                analogWrite(5, pwm);
            case '2':
                receivedChars[0] = '0';
                sscanf(receivedChars, "%d", &pwm);
                analogWrite(6, pwm);
            case '3':
                receivedChars[0] = '0';
                sscanf(receivedChars, "%d", &pwm);
//            default:
//                Serial.println("I don't know what that means.");
        newData = false;

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



There Are So Many Things And Too Many Options!

There are calendars with only pictures of black-furred dogs, bottle openers for the left-handed, overviews of lists of lists of things in the Wikipedia, 9 different USB connectors (plus extensions, plus rare proprietary connectors, times 2 for male and female), there are more human languages and dialects than any one person has even heard of, there are clocks with inverse hours markings, clocks with more than three hands, humans with more than two hands, apples with more than one colour, child safety locks designed to be so weak that they are useless while technically implementing the requirements that they need to have in order to legally be allowed to be called child safety locks, there are programs for children to prevent drug addictions in the future, there are commercial efforts to anchor behaviour in children that makes them more likely to become addicted to certain drugs in the future, there are safety shoes that look like sports shoes. There are ingenious ideas that never go anywhere because the person who had it is not listened to, there are thoughts that come to you after they could and should have come to you. (Like the one that just came to me: This first paragraph looks like it was inspired by a song by Funny van Dannen that I and probably most of the readers of this blog know. I don't think it was.) Anyway. There are soo many things, is what I'm trying to say.

When it comes to living ones life as a human in the 2020s, there is a notably large amount of options, which I herewith declare as too large. I mean options in many different areas. But I'll only name one area here, because it happens to bother me right now and I'm not really in a writing mood today. (You may also see it as an example of too many things: Too many examples to name here.)

There are too many ways to fill your time. There are so many parameters that play a role in deciding what to do at any given time that I find it impossible to choose the right thing simply because it would take, well, probably the rest of my life, to find out what the right decision would be. It would definitely take so long that what the right decision would be would change multiple times in the meantime.

Let's leave out work. If you're employed and have to work certain hours, I think I can make the case that those hours should be treated differently. Let's say you are lucky enough to always know what your job is and what you should do at work and maintain a barrier between work life and private life that allows for such disregarding.

Then, what do you want to do after work, before work, inbetween work? Some broad categories would be to do something to work towards certain goals, like to improve the state and wellbeing of society, your town, your country, the world, do something for friends or family, socialise for personal development and gaining experiences, improve your own health or life, just relax or have fun for now, and so on.

Let's just choose the last one because it's probably the simplest to explore in the context of this text and because hedonistic endeavours are an easy choice and close to my thinking if in doubt.

So, what do you do to relax from work, from the stress that comes with living a modern life and to have a good time?

I took a break from writing this text and was distracted for too long. I've lost my train of thoughts now. Don't expect a continuation. (I will jump on the train and continue this text if/when I get the same thought again.)