User Tools

Site Tools


projects:blinker:work_logs:7_code

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
projects:blinker:work_logs:7_code [2022/02/28 12:37] tjhowseprojects:blinker:work_logs:7_code [2022/03/03 07:17] (current) tjhowse
Line 8: Line 8:
 The message and the code to display the message on the LED must all live within the same 1024 bytes. I must strike a balance between efficient storage of the message, and efficient code to unpack and display that message. Cleverer code would let me crunch the message into less storage, but the code itself would probably take up more space to store the cleverness. The message and the code to display the message on the LED must all live within the same 1024 bytes. I must strike a balance between efficient storage of the message, and efficient code to unpack and display that message. Cleverer code would let me crunch the message into less storage, but the code itself would probably take up more space to store the cleverness.
  
-am to hit a middle point of moderate cleverness and medium message storage density. I think a decent approach is to translate the text to Morse code outside the microcontroller, to keep the display code as small and simple as possible.+aim to hit a middle point of moderate cleverness and medium message storage density. I think a decent approach is to translate the text to Morse code outside the microcontroller, to keep the display code as small and simple as possible.
  
 ===== The Results ===== ===== The Results =====
Line 16: Line 16:
 I wrote a [[https://github.com/tjhowse/blinker/blob/main/code/generate_morse.py|python script]] that converts a provided string into a series of hex bytes in a header file that the microcontroller code uses to store the sequence. I wrote a [[https://github.com/tjhowse/blinker/blob/main/code/generate_morse.py|python script]] that converts a provided string into a series of hex bytes in a header file that the microcontroller code uses to store the sequence.
  
-The input `sos nbn` translates to:+First we take an input message, like **sos** and translate it to Morse code:
  
-<code c+<code> 
-uint8_t const sequence[] = {0x15, 0x77, 0x47, 0x05, 0x5c, 0x5c, 0xc5, 0x05};+     S            O            S 
 +      
 +dit dit dit  dah dah dah  dit dit dit
 </code> </code>
  
-If we write that out in binary in one solid blob (reversing the bit order):+According to the Morse code spec a **dit** is the shortest time window. The gap between tones (or keys, or flashesis the same length as a **dit**. A **dah** is three times that length. The gap between letters is the same length as a **dah**. The gap between words is seven times the length of a **dit**. Taking that all into account we can map a **1** or a **0** to whether the LED should be on at a given point in time:
  
 <code> <code>
-1010100011101110111000101010000000111010001110101010001110100000+  1  0  1  0  1  000 111 0 111 0 111 000  1  0  1  0  1  0000000 
 +|dit| |dit| |dit|   |dah| |dah| |dah|   |dit| |dit| |dit|
 </code> </code>
  
-This is the sequence of on and off states for the LED. We can split it up like this:+Then we flip the bit order and store that as hexadecimal in a C byte array:
  
-<code> +<code c
-   0  1  0  1  000 111 0 111 0 111 000  1  0  1  0  1   0000000... +uint8_t const sequence[] = {0x15, 0x77, 0x47, 0x05, 0x00};
-|dit| |dit| |dit|   |dah| |dah| |dah|   |dit| |dit| |dit|+
 </code> </code>
  
 +==== Display ====
  
 +The display code is very simple:
  
 +<code c>
 +void blink() {
 +  if (sequence[counter>>3] & (0x01 << counter%8)) {
 +    LED_ON;
 +  } else {
 +    LED_OFF;
 +  }
 +  counter = (counter + 1)%(sizeof(sequence)*8);
 +}
 +</code>
  
 +There is some bit-shifty nonsense going on to pick the correct bit out of the sequence. Then we either turn the LED on or off based on the value of the current bit of interest. Then we increment the counter and wrap it back to zero if it went past the end of the sequence.
  
 +==== Power saving ====
  
 +The Attiny10 has some options for reducing its power consumption. We can shut down the analogue to digital converter (ADC) since we're not reading any analogue values, and we can stop TIMER0 as we won't be using it.
  
 +<code c>
 +// Disable ADC:
 +ADCSRA = 0;
 +// Shut down ADC and timer0
 +PRR = 0b11;
 +</code>
  
 +The biggest power saving came from putting the microcontroller to sleep between "frames" of the sequence.
  
 +<code c>
 +void sleep() {
 +  // Tell the watchdog timer to wake us up in 120ms.
 +  wdt_enable(WDTO_120MS);
 +  WDTCSR |= (1<<WDIE | 1<<WDE);
  
 +  // Go to sleep.
 +  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 +  sleep_enable();
 +  sleep_cpu();
 +}
  
 +// This fires when the watchdog timer expires and wakes up the CPU.
 +ISR(WDT_vect) {
 +  sleep_disable();
 +}
 +</code>
  
 +This code is called after the **blink()** function above. It recruits the watchdog timer to fire an interrupt event in 120ms, then goes to sleep. This stops everything in the CPU except the watchdog timer. The contents of RAM are conserved. When the watchdog timer goes off it calls the interrupt service routine (ISR) which disables sleep mode. Then **blink()** fires again and we go back to sleep.
  
-===== Video =====+This approach reduced power consumption by the CPU and LED [[projects:blinker:work_logs:6_power_consumption_testing|by around 95%. ]]
  
-{{ youtube>WTbTDbBAby0?large }}+==== Testing ====
  
-===== Next Time =====+I don't (yet) know how to read Morse code, but I wanted to validate whether I was blinking out the message correctly, and looping around at the end. I wrote some [[https://github.com/tjhowse/blinker/blob/main/code/webcam.py|shockingly dodgy]] OpenCV code to watch the LED through a webcam and decode the Morse: 
 + 
 +{{:projects:blinker:work_logs:pasted:20220228-130034.png}} 
 + 
 +It worked really well! I fed some [[https://en.wikipedia.org/wiki/Lorem_ipsum|Lorem ipsum]] into the sequence generator and slowed the blink interval down to 500ms. I ran it for a few hours and it validated that everything was working as it should. During this testing I measured the longest message I could fit onto the microcontroller alongside the code to display it: 
 + 
 +<code> 
 +lorem ipsum dolor sit amet  consectetur adipiscing elit  donec faucibus  orci  
 +quis iaculis volutpat  lacus mi sollicitudin sem  at dignissim turpis arcu nec  
 +est  fusce elementum vitae risus non aliquam  proin finibus arcu ullamcorper   
 +scelerisque orci egestas  varius mi  nulla bibendum eros sit amet leo  
 +pellentesque lobortis  curabitur a gravida mauris  ac tincidunt ex  phasellus  
 +sagittis congue massa  quis tincidunt felis  nunc et orci eget mi mollis viverra  
 +non in nunc  nullam dapibus pulvinar lectus  ut facilisis neque  vestibulum at  
 +tellus id enim posuere sagittis  nam tincidunt vel nulla eu tempor  abcdef  
 +qwerty asdfg zxcvb 
 +</code> 
 + 
 +Switching from a Logitech C922 Pro to a PS3 eye let me reduce the dit interval to the final value of 125ms and still decode it perfectly. Fun! 
 + 
 +===== Video =====
  
-I don't think there's much point in trying to further improve energy efficiency of the code. The next steps might be adding features like multiple message sequences, and/or improving the space efficiency to fit more/longer messages.+{{ youtube>PKivx9OP3eo?large }}
  
 [<6>] [<6>]
  
projects/blinker/work_logs/7_code.1646051829.txt.gz · Last modified: 2022/02/28 12:37 by tjhowse