WordPress HTTP Error when uploading files

Today I got this error when uploading a video to my WordPress site. I Googled around for a little bit, but the proposed solution wasn’t working.

It turns out that since I am using WordPress docker and a Nginx proxy, this later was complaining that the file was too large 413 Request Entity Too Large when the file was uploaded using the browser uploader, not the WordPress multi-file uploader.

Wordpress Browser Uploader
WordPress Browser Uploader

The solution was easy:
$ vim nginx.conf

http {
...
# Added because uploading large files to WordPress is throwing HTTP error.
client_max_body_size 64M;
...

In my case since I want my setting to take place for all the websites I am hosting with Nginx I applied the setting under the http section.

Enjoy

SET Game Visual Solver

For those of you that have never played SET® before, SET® is a fast-paced game involving 81 unique cards with different combinations of four characteristics (color [red, green, or purple], shape [diamond, oval, or squiggle], shading [hollow, shaded, or filled], and number [1, 2, or 3]). The point of the game is to find as many 3-card “sets” as possible. If the three cards are either all the same or all different in each of the four characteristics, you know you’ve found a set. Twelve cards are presented on the table at a time, and are continually replenished when a set is taken out, until no cards remain in the deck. Sometimes, however, there are no sets in the twelve cards, in which case 3 more cards are dealt. But, how do you know if there really is no set? I aim to solve this problem using image processing software and eventually create a set recognition mobile application.

So, what determines a set? Let’s take the example below (although it is probably one of the most difficult sets in the game). Looking at the cards in terms of the four characteristics, we have the following: color – red, green, and purple (all different); shape – diamond, oval, and squiggle (all different); shading – filled, hollow, shaded (all different); number – 1, 2, 3 (all different). If the cards were exactly the same, but all red, this would also be considered a set, because, in terms of color, they are all the same.

 

There’s the simplified breakdown of how I used an image processing algorithm to determine sets from ordinary jpeg images:

Step 1: Convert the image to black and white and invert the image to calculate the number of shapes per card

Step 2: Compare the pixel surface area of one of the symbols on each card to determine the shape

Step 3: Normalize the RGB color of the images with an equation to figure out the color

Step 4: Convert the images to gray scale and apply a border detection to determine the shading

Step 5: Run the simple algorithm that deduces the sets

For more details, check out the paper I wrote.

Dockerized WordPress + nginx

I recently found myself creating far too may EC2 instances to host simple WordPress (WP) sites like this blog. So I decided to do get a t2.medium in AWS and use docker to run multiple WP sites via an nginx reverse proxy.

The process is easy, go and get

BlackJack Hitting vs. Standing chances

Who has never thought about beating the house in a Casino? As the MIT students said in their documentary Breaking Vegas, it’s not all about the money; but about the feeling of beating a huge corporation in their own game.

It’s very easy to find online the basic strategy for BlackJack, i.e., the correct decision when the only information that you have about the game are your cards, and the dealers facing card. Understanding by correct decision, the one that would minimize your losing chances. Even with this basic strategy the odds against the house are negative, but greatly reduced to a mere -3 $ per 100$ played (aprox). Of course the Breaking Vegas students didn’t just played basic strategy, they also counted cards and played in a team not to raise any attention from the Big Brother.

For this post, I wrote a small Python notebook that computes what are the chances for the different combinations of cards and strategies in their most simplified way, standing and hitting.

The full notebook: https://github.com/Koff/blackjack/blob/master/blackjack_simulation.ipynb

And the results in two images:

 

On the Y-Axis, the starting score of the player, and on the X-Axis, the dealer’s facing up card (11 being the Ace). The color and values represent the chances of winning if the following the strategy.

[Android] Baldur’s Gate II Text Issue

I recently purchased Baldur’s Gate II on Google Play Store, but just after a few minutes of playing, I noticed how small the UI elements and texts were displayed on my LG G3.

Furthermore turning on the setting UI Scaling doesn’t change anything for screens above Full-HD, which is the case of the LG G3.
I decided to then open the file Baldur.ini located in:

/sdcard/Android/data/com.beamdog.baldursgatellenhancededition/files

With Root Explorer (you don’t need a rooted device). Then go to the lines 75 & 76 and change them from:

'Fonts', 'Zoom', '148',
'Fonts', 'Size', '3',

to:

'Fonts', 'Zoom', '248',
'Fonts', 'Size', '6',

If you would also like to increase the upper limit for the FPS by default capped at 30 fps, go to line 70 and change it, note that this can drain your battery:

'Program Options', 'Maximum Frame Rate', '30',

to:

'Program Options', 'Maximum Frame Rate', '60',

My final Baldurs.ini looks like this:

CREATE TABLE options (
section string,
name string,
value string
);
INSERT INTO options ROWS (
'Fonts', 'ko_KR', 'UNBOM',
'Fonts', 'zh_CN', 'SIMSUN',
'Fonts', 'ja_JP', 'MSGOTHIC',
'Fonts', 'ru_RU', 'PERMIAN',
'Fonts', 'uk_UA', 'PERMIAN',
'Graphics', 'version', 'OpenGL ES 3.0 V@84.0 AU@ (CL@) - build 207',
'Graphics', 'renderer', 'Adreno (TM) 330',
'Graphics', 'vendor', 'Qualcomm',
'MOVIES', 'LOGO', '1',
'Graphics', 'Scale UI', '1',
'Game Options', 'Footsteps', '1',
'Game Options', 'Memory Level', '1',
'Game Options', 'Mouse Scroll Speed', '36',
'Game Options', 'GUI Feedback Level', '5',
'Game Options', 'Locator Feedback Level', '3',
'Game Options', 'Bored Timeout', '3000',
'Game Options', 'Always Dither', '1',
'Game Options', 'Subtitles', '1',
'Game Options', 'Keyboard Scroll Speed', '36',
'Game Options', 'Command Sounds Frequency', '2',
'Game Options', 'Selection Sounds Frequency', '3',
'Game Options', 'Effect Text Level', '62',
'Game Options', 'Infravision', '0',
'Game Options', 'Weather', '1',
'Game Options', 'Tutorial State', '1',
'Game Options', 'Attack Sounds', '1',
'Game Options', 'Auto Pause State', '0',
'Game Options', 'Auto Pause Center', '1',
'Game Options', 'Difficulty Level', '3',
'Game Options', 'Suppress Extra Difficulty Damage', '0',
'Game Options', 'Quick Item Mapping', '1',
'Game Options', 'Environmental Audio', '1',
'Game Options', 'Heal Party on Rest', '1',
'Game Options', 'Terrain Hugging', '0',
'Game Options', 'HP Over Head', '0',
'Game Options', 'Critical Hit Screen Shake', '1',
'Game Options', 'Hotkeys On Tooltips', '1',
'Game Options', 'Area Effects Density', '100',
'Game Options', 'Duplicate Floating Text', '1',
'Game Options', 'Tiles Precache Percent', '100',
'Game Options', 'Color Circles', '1',
'Graphics', 'Zoom Lock', '0',
'Game Options', 'Over Confirm Everything', '0',
'Game Options', 'Show Learnable Spells', '1',
'Game Options', 'Render Actions', '2',
'Game Options', 'Confirm Dialog', '1',
'Multiplayer', 'Disable Banters', '1',
'Program Options', 'Disable Cosmetic Attacks', '0',
'Game Options', 'Render Travel Regions', '1',
'Game Options', 'Pausing Map', '0',
'Game Options', 'Extra Feedback', '0',
'Game Options', 'Filter Games', '1',
'Game Options', 'All Learn Spell Info', '0',
'Graphics', 'Hardware Mouse Cursor', '1',
'Game Options', 'Maximum HP', '1',
'Game Options', 'Show Character HP', '1',
'Game Options', 'Nightmare Mode', '0',
'Game Options', '3E Thief Sneak Attack', '0',
'Game Options', 'Cleric Ranger Spells', '1',
'Program Options', 'Font Name', '',
'Program Options', 'Double Byte Character Support', '0',
'Program Options', 'Drop Capitals', '1',
'Program Options', '3D Acceleration', '1',
'Program Options', 'Maximum Frame Rate', '60',
'Program Options', 'Path Search Nodes', '32000',
'Program Options', 'Tooltips', '2147483647',
'Program Options', 'Translucent Shadows', '1',
'Program Options', 'Sprite Mirror', '0',
'Fonts', 'Zoom', '248',
'Fonts', 'Size', '6',
'Program Options', 'Volume Movie', '90',
'Program Options', 'Volume Music', '40',
'Program Options', 'Volume Voices', '100',
'Program Options', 'Volume Ambients', '40',
'Program Options', 'Volume SFX', '80',
'MOVIES', 'INTRO15F', '1',
'MOVIES', 'INTRO', '1',
'Multiplayer', 'Last Protocol Used', '1',
'Game Options', 'Last Save SOA', '000000001-Quick-Save',
'MOVIES', 'BLACKPIT', '1',
'Game Options', 'Last Save TOB', '000000004-Quick-Save-TOB',
'MOVIES', 'REST', '1',
'Window', 'Maximized', '0',
'MOVIES', 'DEATHAND', '1',
'MOVIES', 'POCKETZZ', '1',
'MOVIES', 'SARADUSH', '1',
'MOVIES', 'RESTDUNG', '1'
);

APAC Country List

This is probably the most stupid post of this website up to the moment, but I couldn’t find the information anywhere else; so here it is, the list of country codes for the Asia Pacific region (APAC) and it’s two letters ISO equivalent.

Australia,AU
Brunei,BN
Cambodia,KH
People's Republic of China,CN
Hong Kong,HK
Macau,MO
Fiji,FJ
Indonesia,ID
Japan,JP
Kiribati,KI
North Korea,KP
South Korea,KR
Laos,LA
Malaysia,MY
Marshall Islands,MH
Federated States of Micronesia,FM
Nauru,NR
New Zealand,NZ
Palau,PW
Papua New Guinea,PG
Philippines,PH
Samoa,AS
Singapore,SG
Solomon Islands,SB
Thailand,TH
Timor-Leste,TP
Tonga,TO
Republic of China (Taiwan),TW
Tuvalu,TV
Vanuatu,VU
Vietnam,VT

Maybe it will save someone’s 5 mins.

Car Accelerometer Project using Proteus

In this post, I am presenting one of the projects I did for a class. The idea was that we could set up a micro-controller with two accelerometers on a car so we could get the tilt angles of the car in two axes. The accelerometers output is a PWM where the acceleration measured by each of them is encoded in g’s units (1 g is the gravity acceleration of the earth).

pic_16f88
PIC 16F88

Our micro-controller has to be programmed so we can capture (we will use the Capture pins for this) the two PWM’s generated by the accelerometers, and perform the following tasks:

  1. Calculate the tilting angle in that axis: knowing the duty of the PWM to calculate the tilt angle we do as follows: 2πarcsin((X1X1+X20.5)8)2∗π∗arcsin⁡((X1X1+X2−0.5)∗8). So after capturing the PWM’s duty we should do this operation.
  2. Average the last three angle measurements: The accelerometers measurements are usually noisy, so we want to average the last 5 measures. To do this we simply create a five element array and a pointer to this array so we can keep track of where the last angle was added. In the following task, when I talk about angles I am referring to the average of the angles.
  3. Print the tilt angles to an LCD: A standard two-lines LCD is connected to the uController and we have to output the tilting values previously calculated.
  4. Generate two PMW’s encoding the angle: Once we have calculated the angles we have to also encode them into PWM with a duty of 50% representing an angle of zero degrees and for each degree negative or positive we should add or subtract respectively 1% of the duty. That way an angle of 13 deg. should be encoded as a duty of 63%. This PWM should have a total period of 1 msec.
  5. Record the angles in a EEPROM: To keep historical data, it can be useful in case of accident or other problems, sort of simple black box for cars. To communicate with the EEPROM we will use i2Ci2C.

At the end of this post you can download the source code and the schematic files for Proteus. The schematic looks like this:

As you can see there is another PIC in the figure with two buttons, this PIC generates the PWM’s that the accelerometers would create (Proteus doesn’t include a model for accelerometers). Pressing the buttons you can change the duty generated by the PIC so it would be like tilting the vehicle.

Capturing the PWM’s

The core of the project. To do this we are going to use the CCP modules, CCP stands for Capture Compare, (…) and they are useful because we can set them so when a raising or falling edge is detected (using an interruption) we can activate a timer and capture the next raising or falling edge. This way, we can compute the duty of the two PMW’s.

Let’s take a look at the code.

setup_ccp1(CCP_CAPTURE_RE); // Set-up CCP1 to capture raising
setup_ccp2(CCP_CAPTURE_RE); // Set-up CCP2 to capture raising
setup_timer_1(T1_INTERNAL); // Set-up timer 1, associated to the CCP
 
enable_interrupts(INT_CCP1); // Activate all the interruptions
enable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);

First, we have to set up the PIC so we can capture the Raising Edge, you can set it up to capture the Falling Edge, it doesn’t matter as long as you set it up to something. Then we also have to set up the timer 1; we would use this timer to compute the duty. In my case. I set it up to the internal clock (10 MHz) so it’s fast enough without overflowing in a period of the PWM.

                _      __     ___    _____   _      _____   __     _  
               | |    |  |   |   |  |     | | |    |     | |  |   | |
PWM Signal     | |    |  |   |   |  |     | | |    |     | |  |   | | 
             __| |____|  |___|   |__|     |_| |____|     |_|  |___| |__
Data            0       1      2       4     0        4      1     0

 

Let’s declare the variables we will need for the first PWM’s input:

long fall_1;    // Used to store the instant when the accelerometer 1 falls
long raise_1;   // Used to store the instant when the accelerometer 1 raises   
long X1; // X1 represents the duty (time the pulse is on high) for acce 1
long X2;  // X2 represents the time the pulse is on low for the accelerometer 1
float aceleracin1=0.0; // Accel1, measured in G's calculated from X1 and X2
float angulo1 = 0; // Angle 1 stores the tilt angle of veh. in one of the axis 
int1 change1=0;    // Bool variable to store the current state of the pulse, fo the accelerometer 1

Once we have our variables declared we can use them in the main loop. The math done can be found in the datasheet of the accelerometer (Page 9).

aceleracion1 = (float)1.0*X1/(X1+X2); // Calculate the accel1 in G's from X1 and X2.
aceleracion1 = (aceleracion1 - 0.5)*8;// This transformation is necessary to correctly compute the angle.
angulo1 = 57*asin(aceleracion1);// angle 1 is:(180/pi) * arcsin(accelaration1)

And here is how we calculate the angle from X1 and X2…. But wait, we haven’t set those values! Will do it right now! Programming a microcontroller is a different paradigm from programming in regular C in our PC. Here we are much closer to the hardware and much more constrained in terms of resources. To capture the PWM’s we will use the CCP pins, these pins will call an interruption every time their value is changed, this is a great and easy way of capturing the pulse duty as we can capture the rise and fall of the signal!

The code for the interruption is as follows:

#INT_CCP1                                                                           // Capture HIGH and LOW times of the accelerometer 1
interrupcion_ccp1()
{
   if (change1 == 0)           
   {
      fall_1 = CCP_1; // Capture the moment the CCP_1 goes from high to low.
      change1 = 1;    // Now we get ready to capture hight to low
      setup_ccp1(CCP_CAPTURE_FE);  // Set-up to capture Falling Edge
   }
   else
   {
      X1 = fall_1 - raise_1;  // Calculate X1 as fall1 - raise1. X1 stores the time the pulse stayed on high
      raise_1 = CCP_1;        // Capture the new raising time
      X2 = raise_1 - fall_1;  // Calculate X2 as the time from the previous fall and the actual just captured raise1
      
      change1 = 0;            // Now we are ready to capture the low to high
      setup_ccp1(CCP_CAPTURE_RE);  // set-up to capture the Raising Edge
   }
}

Now it should work! We have to still do the same for the other accelerometer, the code is equivalent just changing the inputs and the variables names to refer to the other data. I will not write it explicitly here but as mentioned above the source code is at the bottom of this post.

Window Average

Now that we now how to capture on pulse duty and calculate is the corresponding angle let’s make something easier but important. Let’s build a five element array where we keep the 5 most recent values. That way we can average that array removing the impact of noise and other artifacts. The following code will do just that:

float bufferAngulo1[5] = {0,0,0,0,0};   // Buffer to record the 5 most recent values of angle 1
float bufferAngulo2[5] = {0,0,0,0,0};   // Buffer to record the 5 most recent values of angle 2
float mediaAngulo1=0;                   // Average of the angle 1
float mediaAngulo2=0;                   // Average of the angle 2
 
int8 punteroBuffer1=0;                  // Pointer for bufferAngulo1
int8 punteroBuffer2=0;                  // Pointer for bufferAngulo2

This will initialize all the variables. Let’s populate them with the data. In the main loop we should add:

bufferAngulo1[punteroBuffer1] = angulo1;    // New element to the buffer
bufferAngulo2[punteroBuffer2] = angulo2;
 
mediaAngulo1 = (bufferAngulo1[0]+bufferAngulo1[1]+bufferAngulo1[2]+bufferAngulo1[3]+bufferAngulo1[4])/5; // Average of the buffer. For loop to be avoided because of delays caused
mediaAngulo2 = (bufferAngulo2[0]+bufferAngulo2[1]+bufferAngulo2[2]+bufferAngulo2[3]+bufferAngulo2[4])/5;
 
punteroBuffer1=punteroBuffer1+1;// Increment the pointers
punteroBuffer2=punteroBuffer2+1;
 
if (punteroBuffer1 >= 4) // Reset the pointers in case they reached the end of the buffer
{
    punteroBuffer1 = 0;                                                    
}
if (punteroBuffer2 >= 4) // Reset also the second pointer to the buffer.
{
    punteroBuffer2 = 0;
}

There you go, this one was easy. We have our buffer and a pointer to the most recently modified item in it so we know where to store the next data. When we make it to the end of the buffer we point to the beginning, making it like a circular buffer. We also calculate the average of the buffer. We avoid using a for loop as it can hurt the performance.

LCD

Now we should be able to display the results on the LCD connected to our micro-controller. This is fairly easy because the LCD manufacturer provides a library to make it’s access easy. So with a simple #include lcd.h we can use the functions printf on the screen. The code in the main loop should look like this:

lcd_putc('\f');  // This flushes the screen
printf(lcd_putc, "Angle 1: %3.1f",mediaAngulo1);
lcd_putc('\n');                     // Print the average of the buffer to the LCD
printf(lcd_putc, "Angle 2: %3.1f",mediaAngulo2);

One lest tick on the to-do list to go.

Generate the PWM’s

Turn off the PWM generation. Let’s recall what we had to do. We have to generate two PWM with period 100 msec. Each of them is going to encode one angle assigning a 50% duty to an angle of zero deg. and adding (or subtracting) a perceptual point for each degree.

As before let’s declare the variables that we are going to use:

int8 pwmOut1H = 0;  // Time that the output PWM 1 should stay in HIGH
int8 pwmOut1L = 0;  // Time that the output PWM 1 should stay in LOW
int8 pwmOut2H = 0;  // Time that the output PWM 2 should stay in HIGH
int8 pwmOut2L = 0;  // Time that the output PWM 2 should stay in LOW

Now inside the main loop, we should populate the variables with its right values:

pwmOut1H = (int) (50.0+angulo1); // The angle is encoded as a PWM where 50% duty represents 0 deg. so for example 40% duty represents -10 deg. angle in this axis
pwmOut1L = 100 - pwmOut1H; // The time that the PWM should be in LOW, is 100 (total width) minus the time that it should stay in HIGH
pwmOut2H = (int) (50.0+angulo2);// The angle is encoded as a PWM where 50% duty represents 0 deg. so for example 60% duty represents +10 deg. angle in this axis
pwmOut2L = 100 - pwmOut2H;

So by now, we have the values that we want our variables to be on HIGH and LOW. But we still have to output these values on the outputs. We will use the timer2 interruption to do that. This timer is setup so it will overflow every msec. since the period is 100 msec. we should a precision of 1 in 100, not great but not too bad either!

With this timer interruption overflowing we will check to see if how much time we have left to stay HIGH or LOW and set the output accordingly. Let’s get to the code.

#INT_TIMER2
interrupcion_timer2()           // This interruption is used to change the PWM at the output that encode the angles
{
   pwmOutCounter1 = pwmOutCounter1 + 1;     // Increment both counters
   pwmOutCounter2 = pwmOutCounter2 +
   // PWM 1
   if (pwmOutCounter1 <= pwmOut1H)           // If we havent reached the time that PMW1 is supposed to stay in high, then we keep it in high
   {
       output_bit( PIN_A0, 1);           // Keep PIN A0 in HIGH
   }
      
   if ((pwmOutCounter1 > pwmOut1H) && (pwmOutCounter1 < pwmOut1H+pwmOut1L) // In case the counter has been enough time on HIGH but has not been enough time on LOW we keep it to low
   {
       output_bit( PIN_A0, 0);      // Keep PIN A0 in LOW
   }
 
   if (pwmOutCounter1 >= pwmOut1H+pwmOut1L)  // In case we have arrived to the end we set the counter to zero to start over
   {
       pwmOutCounter1 = 0;
   }
}

The code is straight forward. Every millisecond we check where we are in terms of time and we select the outputs accordingly.

EEPROM

The last part of our project is to write the average of the last 5-measurements into an EEPROM via i2C. To do this we are going to use a library to simplify the code. We will call the write function every second; for this we will set-up the timer 3 to overflow every second. We do this with the following code:

#include <2416.C>    // Library for the EEPROM
long int address = 0;   // address to write in the EEPROM, set to 0 to start at the beginning of the memory
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);  // Use timer 3 to write to the EEPROM when overflow

Now we have an interrupt every second, so we will need to write the code to write into the EEPROM. We will use the variable address to keep track of the last position written. The code:

#INT_TIMER3                                    
interrpcion_timer3()
{
   write_ext_eeprom(address, (signed int)mediaAngulo1);             // Write the average of the angle 1 to the EEPROM
   address = address + 1;                               // Point to the next memory position
   write_ext_eeprom(address, (signed int)mediaAngulo2);             // Write the average of the angle 2 to the EEPROM
   address = address + 1;                           // Point to the next memory position
 
   if (address >= 0x7FF)
   {
      address = 0;                              // If the end of the memory is reached, start overwriting from the beginning
   }
}

This concludes my small project. The source code can be download in here. You should open the files with proteus. I added another PIC and some switches to emulate the pulses coming from the accelerometers.

Hope you enjoyed!

Home Temperature Monitor MSP 430 + Raspberry Pi

Introduction

I am going to share a small project I did a few days ago. I wanted to use the Raspberry Pi to monitor the temperature in my house and ideally to do it over the internet, so I started looking for a temperature sensor. The first problem is that the Raspberry Pi doesn´t have an Analog to Digital converter, so its GPIO are strictly digital, meaning that simple temperature sensors that output the temperature as a voltage in a range cannot be used directly.

So looking around my house I discovered I had a LaunchPad MSP430 (version G2231) from Texas Instrument, which comes luckily equipped with an internal temperature sensor. I thought I could connect the MSP 430 to the Raspberry Pi so that the latter could read the temperature values from the former. Then I could put the data in a MySQL database on the Rpi side and build a web server to plot that data.

MSP 430

First, we need to set up the MSP 430 to read the temperature values from its internal sensor using an ADC. Then we can set up the i2C  in slave transmitter mode (the RPi can only work as a master). If you are starting with the MSP 430, I would recommend taking a look at its user guide and then to analyze some of the examples that come with Code Composer. It has a great debugger that allows you to stop the execution at any moment, look and modify the registers, step by step execution, etc.

Temperature Sensor + ADC

The temperature sensor is described briefly on page 550 of the user guide. It can be sampled by the ADC if we select the register INCx1010 INCHx=1010. Let’s take a look at the code. I used a lot of it from the examples, is not bad if you know what you are doing, but you need to be careful and can´t simply copy and paste different examples.

#include <msp430g2231.h>         // It has the definitions for all the registers, modes, etc.
 
unsigned int temp;               // holds ADC result
char SLV_Data = 0;                     // Variable for transmitted data   
 
int main(void)
{
      WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog
 
      ADC10CTL1 = INCH_10 + ADC10DIV_3;         // Select Channel 10 and clk divided by 4
      ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;      // Set Reference value Enable interrupts and turns on the ADC10
      TACCR0 = 30;                              // Delay to allow Ref to settle. Definition in the user guide
      TACCTL0 |= CCIE;                          // Interrupt enable for the Capture Compare
      TACTL = TASSEL_2 | MC_1;                  // Source clock TACLK = SMCLK, Count mode :Up
 
      _EINT();                                  // General Interrupt Enable
 
while(1)
  {
     ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
     temp = ADC10MEM - 50;
  }
}
 
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
        SLV_Data = temp-50;             // To the data readed from the ADC (range 0-1023) substract 50.
}
 
#pragma vector=TIMERA0_VECTOR
__interrupt void ta0_isr(void)
{
    ADC10CTL0 |= ENC;                     // Enable conversion
    TACTL = 0;                            // Clear the Timer A control register
}

This is the code for the ADC conversion. If you have any doubt about what I did you can ask in the comments, and look at the User Guide from Texas Instrument. Also, I found really useful to look at the msp430g2231.h To access that file you can hold the ctrl button while you click on it, and it will open in a new tab. There you can see what all those TACTL, ADC10CTL0, etc. are

There is one thing to be noted and is that the value read by the ADC is 10 bits long, however for 2i2 we need packages of 8 bits of data, of course we could split the package of 10 into two packages of 8 bits, one of them containing 6 dummy zeroes, but here comes the trick: the whole range of the thermometer is not needed. A normal house temperature doesn´t range from -50 C to +80 C, so we can omit the two most significant bits (MSB), and make substract or add a value to the variable so a normal temperature stays approx. in the middle range (around 128). That’s why I subtracted 50, so now cold temperatures in the house correspond to 90 while warm ones stay around the 140.

i2C Communication Implementation

Now that we have the value of the temperature in the variable LDta SLVData we need to send it to the Raspberry Pi. We are going to use i2Ci2C for its simplicity. Other protocols such as UART or SPI are somewhat more complicated to implement.

First let’s take a look at the code, here I combined the previous part with this new one so this is the final code:

#include <msp430g2231.h>
 
char SLV_Data = 0;                     // Variable for transmitted data
char SLV_Addr = 0x90;                  // Address is 0x48
int I2C_State = 0;                     // State variable
 
unsigned int temp;            // holds  ADC result
unsigned int IntDegC;
 
int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog
  if (CALBC1_1MHZ==0xFF)               // If calibration constants erased
  {
    while(1);                          // do not load, trap CPU!!
  }
  DCOCTL = 0;                          // Select lowest DCOx and MODx settings
  BCSCTL1 = CALBC1_1MHZ;               // Set DCO
  DCOCTL = CALDCO_1MHZ;
 
  ADC10CTL1 = INCH_10 + ADC10DIV_3;         // Temp Sensor ADC10CLK/4
  ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;      // Enable interrupts.
  TACCR0 = 30;                              // Delay to allow Ref to settle
  TACCTL0 |= CCIE;                          // Compare-mode interrupt.
  TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
 
  P1OUT = 0xC0;                        // P1.6 & P1.7 Pullups
  P1REN |= 0xC0;                       // P1.6 & P1.7 Pullups
  P1DIR = 0xFF;                        // Unused pins as outputs
  P2OUT = 0;
  P2DIR = 0xFF;
 
  USICTL0 = USIPE6+USIPE7+USISWRST;    // Port & USI mode setup
  USICTL1 = USII2C+USIIE+USISTTIE;     // Enable I2C mode & USI interrupts
  USICKCTL = USICKPL;                  // Setup clock polarity
  USICNT |= USIIFGCC;                  // Disable automatic clear control
  USICTL0 &= ~USISWRST;                // Enable USI
  USICTL1 &= ~USIIFG;                  // Clear pending flag
  _EINT();
 
  while(1)
  {
     ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
     temp = ADC10MEM - 50;
 
  }
}
 
//******************************************************
// USI interrupt service routine
//******************************************************
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
  if (USICTL1 & USISTTIFG)             // Start entry?
  {
    P1OUT |= 0x01;                     // LED on: Sequence start
    I2C_State = 2;                     // Enter 1st state on start
  }
 
  switch(I2C_State)
    {
      case 0: //Idle, should not get here
              break;
 
      case 2: //RX Address
              USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, RX Address
              USICTL1 &= ~USISTTIFG;   // Clear start flag
              I2C_State = 4;           // Go to next state: check address
              break;
 
      case 4: // Process Address and send (N)Ack
              if (USISRL & 0x01)       // If read...
                SLV_Addr++;            // Save R/W bit
              USICTL0 |= USIOE;        // SDA = output
              if (USISRL == SLV_Addr)  // Address match?
              {
                USISRL = 0x00;         // Send Ack
                P1OUT &= ~0x01;        // LED off
                I2C_State = 8;         // Go to next state: TX data
              }
              else
              {
                USISRL = 0xFF;         // Send NAck
                P1OUT |= 0x01;         // LED on: error
                I2C_State = 6;         // Go to next state: prep for next Start
              }
              USICNT |= 0x01;          // Bit counter = 1, send (N)Ack bit
              break;
 
      case 6: // Prep for Start condition
              USICTL0 &= ~USIOE;       // SDA = input
              SLV_Addr = 0x90;         // Reset slave address
              I2C_State = 0;           // Reset state machine
              break;
 
      case 8: // Send Data byte
              USICTL0 |= USIOE;        // SDA = output
              USISRL = SLV_Data;       // Send data byte
              USICNT |=  0x08;         // Bit counter = 8, TX data
              I2C_State = 10;          // Go to next state: receive (N)Ack
              break;
 
      case 10:// Receive Data (N)Ack
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack
              I2C_State = 12;          // Go to next state: check (N)Ack
              break;
 
      case 12:// Process Data Ack/NAck
              if (USISRL & 0x01)       // If Nack received...
              {
                P1OUT |= 0x01;         // LED on: error
              }
              else                     // Ack received
              {
                P1OUT &= ~0x01;        // LED off
                SLV_Data++;            // Increment Slave data
              }
              // Prep for Start condition
              USICTL0 &= ~USIOE;       // SDA = input
              SLV_Addr = 0x90;         // Reset slave address
              I2C_State = 0;           // Reset state machine
              break;
    }
 
  USICTL1 &= ~USIIFG;                  // Clear pending flags
}
 
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
        SLV_Data = temp-50;
}
 
#pragma vector=TIMERA0_VECTOR
__interrupt void ta0_isr(void)
{
    ADC10CTL0 |= ENC;
    TACTL = 0;
}

Now, this part is slightly more complicated. Usually, people rely on a library to ease i2C operations, here the whole implementation of the i2C state machine is displayed. You don’t really need to go through all the operations, but if you are curious, with the user guide you should be able to decode what the code is doing. Is also complicated to debug an i2C slave transmitter code since you need a master to generate the clock signal and to send the address, etc.

Once this code is compiled and pushed into the MSP 430, we are ready to jump to the RPi side.

Raspberry Pi

The first thing to do in the RPi is the interconnection for the i2Ci2C protocol. The pin 1.6 is SCL and the pin 1.7 is SDA so the should be connected respectively to Pin 5 and Pin 3 of the RPi (Model B 512 Mbytes RAM).

SDA and SCL lines inside the square

We should also add a pull-up resistor on those lines to ensure the pins are never floating. I choose values of 2KΩ2KΩ and they are working just fine.

Turn for the software side on the raspberry pi. First is to add the i2Ci2C modules to the kernel. I will not enter on much more detail here. There is an excellent guide that will take you through the process here.

After that, if you run $ i2cdetect -y 1 you should see your MSP430 listening for your orders.

pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Now is the turn for the python code to obtain the values every minute (you can choose whatever you want). I decided to store the value in a MySQL database. I found this guide, that was perfect! I did mostly the same except for adding the field Zone, as I only had one thermometer, it is not needed.

sudo apt-get install mysql-server python-mysqldb

Once completed we can go ahead:

$ mysql -u root -p
Enter password:
mysql> CREATE DB temps;
mysql> USE temps;
mysql> CREATE USER 'monitor'@'localhost' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON temps.* TO 'monitor'@'localhost'
mysql> FLUSH PRIVILEGES;
mysql> CREATE TABLE tempdat (tdate DATE, ttime TIME, temperature NUMERIC);
#!/usr/bin/env python
import time
import os
import sys
import MySQLdb
import smbus
 
bus = smbus.SMBus(1)
address = 0x48;
 
db = MySQLdb.connect("localhost", "monitor", "password", "temps")
curs=db.cursor()
 
def readTemp():
        temp = bus.read_byte(address)
        return temp
 
while True:
    time.sleep(60)    # wait for 60 seconds to record next temperature
    temp = readTemp();
     
    print (temp)  # Used for debugging
    time.sleep(1)  
    curs.execute ("""INSERT INTO tempdat values(CURRENT_DATE(), NOW(), %s)""",temp)
 
    db.commit()

All pretty simple stuff. Now are system is mainly completed but data is useless unless we can access it! So I created a web server that will serve a website plotting the data. Now we have our database ready to be populated. Lets then get to our python code.

Web server to plot the data

First, we need to install apache on the RPi. To do so I followed this guide from Instructables. After, you should be able to introduce the local IP address of your RPi, in my case 192.168.0.103 and see the “it works!” website located in the /var/www of your RPi.

To plot the data I used the library Charts.JS I simply downloaded the .js file from GitHub and put it in the folder /var/www. And then I coded this small website.

<!doctype html>
<html>
  <head>
    <?php
        ini_set('display_errors', 'On');
        error_reporting(E_ALL);
        // Create connection
        $con=mysql_connect("localhost","monitor","password","temps");
 
        // Check connection
        if (mysqli_connect_errno())
        {
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
        }
        
        // Query the data from the last two days of temperatures
        $data = mysql_query("SELECT * FROM temps.tempdat WHERE tdate >= DATE_SUB(CURRENT_DATE, INTERVAL 2 DAY)");
 
        if (!$data) { // add this check.
            die('Invalid query: ' . mysql_error());
        }
 
        $Temp = array();
        $avgTemp = array();
        $contador = 0;
 
        // Get all the temperature records in array
        while($info = mysql_fetch_array( $data ))
        {
            $Temp[$contador] = $info['temperature'];
            $contador = $contador + 1;
        }
        
        $avgTemp = array_fill(0, count($Temp), 0);  // average temperature initialized to zeroes
        $tempData="[";      // This is the way the data should be given to JS [1,2,3,4...]
        $tempLabel = "[";
 
        for ($i = 0; $i < count($Temp) - 10; $i=$i+10) {
            for ($j = 0; $j < 10; $j++){
                $avgTemp[$i] += 0.10 * intval($Temp[$i+$j]);
            }
        
            $tempData .= (string)$avgTemp[$i];
            $tempData .= ",";
  
            $tempLabel .= "\".";  // I dont care about the labels, so I used ".." if you want, you can put the date records in here.
            $tempLabel .= ".\"";
            $tempLabel .= ",";
 
        }
 
        $tempData = substr($tempData, 0, -1); // Remove the last comma of the string    [1,3,5,6,   =>    [1,3,5,6
        $tempLabel= substr($tempLabel, 0, -1);
 
        $tempData .= "]";  // Close the bracket,    [1,3,5,6  =>  [1,3,5,6]
        $tempLabel .= "]";
?>
 
    <title>Temperatura de la Casa</title>
    <script src="./Chart.js" type="text/javascript"></script>
    <meta name = "viewport" content = "initial-scale = 1, user-scalable = no">
      <style>
        canvas{
        }
      </style>
    </head>
  <body>
    <canvas id="canvas" height="600" width="1200"></canvas>
    <script>
 
      var lineChartData = {
      labels :  <?php echo $tempLabel ?>,
      datasets : [
 
      {
      fillColor : "rgba(151,187,205,0.5)",
      strokeColor : "rgba(151,187,205,1)",
      pointColor : "rgba(151,187,205,1)",
      pointStrokeColor : "#fff",
      data : <?php echo $tempData ?>
      }
      ]
 
      }
 
      var myLine = new Chart(document.getElementById("canvas").getContext("2d")).Line(lineChartData);
 
    </script>
  </body>
</html>

Conclusion

This concludes my project. RPi + MSP430 +  i2C + MySQL + PHP + JavaScript, really complete, multidisciplinary and fun project! Comment if you like. Here is a capture of the temperature inside my house!