Categories
Uncategorized

Repairing Electronics Rant

So, I have this Samsung NP900X3A laptop that I used for about 4 years. It started to have random glitches when I was about to start some very important work, so I decided to get a new laptop. I never sold or scrapped it, and recently I took it out of its neoprene bag, cleared the disk, installed a clean Ubuntu 20.04 and it worked like a charm…

…ish. Well, obviously it’s old, the battery had seen better days, but for work when it’s on power supply, it is a very decent device. Until recently:

I plugged it in, opened the lid, pressed power aaaand: it won’t boot.

Okay, my first though: The SSD died. Nothing catastrophic, a 120 GB mSATA costs about 45 €. So I booted into a Linux live system, quickly installed smartmontools (smartctl), opened my favourite smartctl reminder wiki page (seriously, man pages are still the best, but they’re so long!), and found that all tests passed. Well, the drive was old, yes, but still intact. fsck did not complain either.

But it still wouldn’t boot.

So, I opened the BIOS menu. First suspect: time and date were off by years. Aha! The BIOS battery, or actually, the battery for the real time clock (RTC), had died. And that set everything to defaults. Including: UEFI [DISABLED]. Well, you could’ve printed something, you nob…

Anyways: Enabled it, it boots again.

Here’s the culprit: Empty, unmarked

So, what about the battery? It’s obviously a coin cell on two wires with a plug. Nice idea, very accessible. Really. But at least some documentation would have been nice. No label, no print, nothing.

Measuring reveals: diameter 16 mm, thickness maybe a little below 2 mm. Hard to tell exactly while still in the rubber protection. So, could it just be a CR1616 or CR1620 cell with some spot welded soldering pins enclosed in some heat-shrink tubing?

No way to get the original part anymore. Samsung stopped selling laptops for quite a while in Germany (or even the whole EU, though it may have recently restarted again). Ebay has some offers. Anything from 5 to 150 € (seriously, it’s a coin cell, who charges more than 10 € for that?).

I decided to get one full package (cell, heat-shrink, wires and connector) for 5 € and a bare cell with spot welded solder pads for 2 €.

Got the wrong cell for 5 € (for a Samsung P20). Would’ve fit if it wasn’t for the connector.
CR1616 for 1,59 € to the rescue!

So, where’s the rant? Here:

COMPLEX ELECTRONIC DEVICES “BREAK” BECAUSE OF < 5 € COMPONENTS AND YOU NEED A F****ING PhD IN BATTERIES AND EBAY TO FIND REPLACEMENT PARTS BECAUSE NOONE THOUGHT OF PRINTING “CHECK UEFI SETTINGS” OR OF PRINTED LABELS ON A BATTERY OR A SIMPLE REMARK IN THE USER MANUAL (“YEAH, AND BTW, THE BATTERY IS A CR1616 FOR 84 CENTS WITH A MOLEX CONNECTOR, YOU’RE WELCOME.”)???

By now, this laptop would’ve been thrown out twice by every single average capable consumer in the world. And honestly, I cannot blame them. Right to repair will have a loooooong way to go.


So yeah: Turns out the coin cell actually was a CR1616.

Cut off the wires, placed heat-shrink, soldered to replacement cell (not to the cell directly but to the solder tails), pack all in heat-shrink and voilà: a 1,65 € (+some solder and heat-shrink) replacement for a 10 year old laptop.

Categories
Uncategorized

A Toy Traffic Signal with an Arduino Nano

My kid likes traffic lights. So I decided to quickly build one. I started off on a breadboard with a bunch of components:

  • 3 × LEDs (red, amber, green)
  • 3 × 470 Ω Resistors (only had two, so the third are actually 2 1 kΩ in parallel)
  • 1 × 10 kΩ Resistor
  • 1 × Pushbutton Switch
  • 1 × Arduino Nano (actually a clone)

The circuitry is relatively simple. All LEDs’ cathodes point to ground (GND) through a 470 Ω resistor each to limit current and not blow anything. The anodes are connected individually to the digital port D3 through D5.

The button works such that, if open, it connects digital port D2 via a 10 kΩ resistor to the 3.3 V output of the Nano. When it closes, it’s supposed to pull D2 to GND, so it’s directly connected to it. The resistor serves to limit the current and – again – not blow anything.

That’s it. Now, for example, pulling D3 high (i.e. applying 3.3 V) will switch on the red LED. Likewise for D4 on the yellow and D5 on the green LED. Let’s introduce some human readable names instead of D2 through D5:

#include <Arduino.h>

#define BUTTON      D2
#define LED_RED     D3
#define LED_YELLOW  D4
#define LED_GREEN   D5

In the setup() function, I set the button pin as an input and the LED pins as outputs:

void setup() {
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_YELLOW, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(BUTTON, INPUT);
}

To control the traffic light logic, I scribbled a Moore machine on a piece of paper, starting with a pedestrian traffic light (only red and green).

Moore machine with two states (red and green), initialised in the red state. Looped arrows indicate idle non-transition.

The machine starts in the red-light state. It remains there, unless the button is pressed. Then, it instantly switches to green (a pedestrian’s dream) where it remains until the button is pressed again.

This behaviour can be implemented by putting a simple switch case statement in the main loop() and implementing a state variable:

// States
enum states{ ST_R,
             ST_G
           }

void setState(bool red, bool green) {
  digitalWrite(LED_RED, red ? HIGH : LOW);
  digitalWrite(LED_GREEN, green ? HIGH : LOW);
}

void setRed() { setState(true, false); }
void setGreen() { setState(false, true); }

void loop() {
  switch(machineState) {
    case ST_R:
      // Serial.write("State: Red");
      setRed();
      break;
    case ST_G:
      // Serial.write("State: Green");
      setGreen();
      break;
}

Why use this enum thingy, you say? You could just use an int and call your states 0, 1, 2, etc., you say? Yes, you could also sign up for the biannual global brainfuck contest.

But, hey, wait, there are no transitions!

Exactly! For that purpose I’ll use an interrupt.

Just add one more function:

void ISR_ButtonToggle() {
  Serial.write("BUUUUTTON!\n");
  if(machineState == ST_R) {
    machineState = ST_G;
  } else if (machineState == ST_G) {
    machineState = ST_R;
  }
}

And don’t forget to actually bind the interrupt function in setup():

attachInterrupt(digitalPinToInterrupt(BUTTON), ISR_ButtonToggle, FALLING);

Now that’s a simple and pretty useless traffic light (well, I still like that I can switch it to green at will, this really should be the default pedestrian crossing light!).

But it is easy to extend. You want a yellow state? Add a yellow state ST_Y to your state machine (the switch case statement). Or you want it to show yellow before switching from green to red? Add a yellow-before-red state ST_Y_R. Or you really like to let people wait? Add a wait state. Oh, and of course you can call your states whatever you want.

Here is my full implementation of a non-pedestrian red-yellow-green traffic light as it is stated in the German traffic rules:

  • From red to green, with wait periods, go through
    • Red
    • Red+Yellow (yes, red+yellow, yellow meaning “get ready” and red meaning “not too fast, it’s still red, Freundchen!”)
    • Green
  • From green to red, with wait periods, go through
    • Green
    • Yellow (yes, only yellow. The meaning is disputed. Reasonable people say it means “Prepare to stop, if you can’t make it in time, it’s okay.” while idiots say it means “STEP ON THE GAS!”)
    • Red (This is also sometimes disputed, I won’t go into this.)
#include <Arduino.h>

#define BAUDRATE      9600
#define BUTTON        DD2
#define LED_RED       DD3
#define LED_YELLOW    DD4
#define LED_GREEN     DD5

// Transition delays
#define BLINK_WAIT        7
#define DELAY_R_RY        250
#define DELAY_RY_G        250
#define DELAY_G_Y         250
#define DELAY_Y_R         250
#define DELAY_R_G         250
#define DELAY_G_R         250

// States
enum states{ ST_R,
             ST_RY,
             ST_G,
             ST_Y,
             WAIT_R_RY,
             WAIT_RY_G,
             WAIT_G_Y,
             WAIT_Y_R,
             WAIT_G_R,
             WAIT_R_G };

states machineState = ST_R;
bool builtin_led = false;

void setState(bool red, bool yellow, bool green) {
  digitalWrite(LED_RED, red ? HIGH : LOW);
  digitalWrite(LED_YELLOW, yellow ? HIGH : LOW);
  digitalWrite(LED_GREEN, green ? HIGH : LOW);
}

void setRed() { setState(true, false, false); }
void setRedYellow() { setState(true, true, false); }
void setYellow() { setState(false, true, false); }
void setGreen() { setState(false, false, true); }

void blinkDelay(int delay_ms) {
  for(int i=0; i<BLINK_WAIT; i++){
    delay(delay_ms);
    digitalWrite(LED_BUILTIN, builtin_led);
    Serial.write(".");
    builtin_led = !builtin_led;
  }
  Serial.write("\n");
}

void ISR_ButtonToggle() {
  Serial.write("BUUUUTTON!\n");
  if(machineState == ST_R) {
    machineState = WAIT_R_RY;
  } else if (machineState == ST_G) {
    machineState = WAIT_G_Y;
  }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_YELLOW, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  digitalWrite(LED_RED, LOW);
  digitalWrite(LED_YELLOW, LOW);
  digitalWrite(LED_GREEN, LOW);
  pinMode(BUTTON, INPUT);

  attachInterrupt(digitalPinToInterrupt(BUTTON), ISR_ButtonToggle, FALLING);

  Serial.begin(BAUDRATE);
  Serial.write("Ready.\n");
}

void loop() {
  switch(machineState) {
    case ST_R:
      // Serial.write("State: Red");
      setRed();
      break;
    case ST_G:
      // Serial.write("State: Green");
      setGreen();
      break;
    case WAIT_R_RY:
      Serial.write("Wait: Red->RedYellow\n");
      blinkDelay(DELAY_R_RY);
      machineState = ST_RY;
      break;
    case ST_RY:
      setRedYellow();
      machineState = WAIT_RY_G;
      break;
    case WAIT_RY_G:
      Serial.write("Wait: RedYellow->Green\n");
      blinkDelay(DELAY_RY_G);
      machineState = ST_G;
      break;
    case WAIT_G_Y:
      Serial.write("Wait: Green->Yellow\n");
      blinkDelay(DELAY_G_Y);
      machineState = ST_Y;
      break;
    case ST_Y:
      setYellow();
      machineState = WAIT_Y_R;
      break;
    case WAIT_Y_R:
      Serial.write("Wait: Yellow->Red\n");
      blinkDelay(DELAY_Y_R);
      machineState = ST_R;
      break;
    case WAIT_R_G:
      Serial.write("Wait: Red->Green\n");
      blinkDelay(DELAY_R_G);
      machineState = ST_G;
      break;
    case WAIT_G_R:
      Serial.write("Wait: Green->Red");
      blinkDelay(DELAY_G_R);
      machineState = ST_R;
      break;
    default:
      Serial.write("Woops...\n");
      machineState = ST_R;
  }
}

Is this the best way to write this? Hell, no! Is it even a good tutorial? You tell me. I think it’s fairly simple and shows some evenly common concepts:

  • Using IO pins
  • Wiring LEDs without burning them or the output pin
  • Wiring a button*
  • Using #define statements to make code more readable and lives more easy
  • Using some abstraction (setState(), setRed(), etc.)
  • Using a state machine as an extensible concept
  • Using an interrupt (Why use an interrupt? Because it interrupts your code exactly when the button is pressed. It makes your little circuit very responsive and – at least in this example – it is even easier to write.)

*) You may encounter a problem that goes by the name “bouncing” (or in the beautiful German language “prellen”). That’s when your button press toggles multiple interrupts in a row. Try putting a capacitor in parallel with the resistor, or as a bad software hack, add a 5 ms delay (delay(5);) to the end of your interrupt function.

Anyways: My girl loves it. Ha! You thought she’d be a boy because it was a traffic light. Shame on you!